summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr')
-rw-r--r--private/nw/rdr/attach.c5169
-rw-r--r--private/nw/rdr/cache.c698
-rw-r--r--private/nw/rdr/callback.c190
-rw-r--r--private/nw/rdr/cleanup.c591
-rw-r--r--private/nw/rdr/close.c571
-rw-r--r--private/nw/rdr/const.h166
-rw-r--r--private/nw/rdr/convert.c732
-rw-r--r--private/nw/rdr/convert.h33
-rw-r--r--private/nw/rdr/create.c3398
-rw-r--r--private/nw/rdr/create4.c2075
-rw-r--r--private/nw/rdr/crypto.h139
-rw-r--r--private/nw/rdr/data.c373
-rw-r--r--private/nw/rdr/data.h238
-rw-r--r--private/nw/rdr/debug.c780
-rw-r--r--private/nw/rdr/deviosup.c153
-rw-r--r--private/nw/rdr/dir.c1672
-rw-r--r--private/nw/rdr/encrypt.c322
-rw-r--r--private/nw/rdr/errorlog.c201
-rw-r--r--private/nw/rdr/except.c160
-rw-r--r--private/nw/rdr/exchange.c4887
-rw-r--r--private/nw/rdr/exchange.h114
-rw-r--r--private/nw/rdr/fileinfo.c2905
-rw-r--r--private/nw/rdr/filobsup.c175
-rw-r--r--private/nw/rdr/fragex.c783
-rw-r--r--private/nw/rdr/fsctl.c5930
-rw-r--r--private/nw/rdr/fspdisp.c232
-rw-r--r--private/nw/rdr/init.c460
-rw-r--r--private/nw/rdr/ipx.c1749
-rw-r--r--private/nw/rdr/kdext/dirs26
-rw-r--r--private/nw/rdr/kdext/ntsd/makefile6
-rw-r--r--private/nw/rdr/kdext/ntsd/nw.def15
-rw-r--r--private/nw/rdr/kdext/ntsd/nw.rc12
-rw-r--r--private/nw/rdr/kdext/ntsd/sources49
-rw-r--r--private/nw/rdr/kdext/nwrdrkd.c2223
-rw-r--r--private/nw/rdr/kdext/windbg/makefile6
-rw-r--r--private/nw/rdr/kdext/windbg/nwdbg.def15
-rw-r--r--private/nw/rdr/kdext/windbg/nwdbg.rc12
-rw-r--r--private/nw/rdr/kdext/windbg/sources51
-rw-r--r--private/nw/rdr/lock.c1357
-rw-r--r--private/nw/rdr/lockcode.c168
-rw-r--r--private/nw/rdr/makefile6
-rw-r--r--private/nw/rdr/ndsfsctl.c2128
-rw-r--r--private/nw/rdr/ndslogin.c3365
-rw-r--r--private/nw/rdr/ndsprocs.h822
-rw-r--r--private/nw/rdr/ndsread.c1190
-rw-r--r--private/nw/rdr/nodetype.h63
-rw-r--r--private/nw/rdr/nwrdr.rc11
-rw-r--r--private/nw/rdr/pid.c454
-rw-r--r--private/nw/rdr/procs.h1830
-rw-r--r--private/nw/rdr/read.c2838
-rw-r--r--private/nw/rdr/scavengr.c689
-rw-r--r--private/nw/rdr/security.c1009
-rw-r--r--private/nw/rdr/sources90
-rw-r--r--private/nw/rdr/string.c378
-rw-r--r--private/nw/rdr/strucsup.c3127
-rw-r--r--private/nw/rdr/struct.h1357
-rw-r--r--private/nw/rdr/synch.txt74
-rw-r--r--private/nw/rdr/timer.c537
-rw-r--r--private/nw/rdr/util.c385
-rw-r--r--private/nw/rdr/volinfo.c1278
-rw-r--r--private/nw/rdr/workque.c829
-rw-r--r--private/nw/rdr/write.c3063
62 files changed, 64359 insertions, 0 deletions
diff --git a/private/nw/rdr/attach.c b/private/nw/rdr/attach.c
new file mode 100644
index 000000000..099cfc3d1
--- /dev/null
+++ b/private/nw/rdr/attach.c
@@ -0,0 +1,5169 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Attach.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to connect and disconnect from a server.
+
+Author:
+
+ Colin Watson [ColinW] 10-Jan-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h> // rand
+
+//
+// The number of bytes in the ipx host address, not
+// including the socket.
+//
+
+#define IPX_HOST_ADDR_LEN 10
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+VOID
+ExtractNextComponentName (
+ OUT PUNICODE_STRING Name,
+ IN PUNICODE_STRING Path,
+ IN BOOLEAN ColonSeparator
+ );
+
+NTSTATUS
+ExtractPathAndFileName(
+ IN PUNICODE_STRING EntryPath,
+ OUT PUNICODE_STRING PathString,
+ OUT PUNICODE_STRING FileName
+ );
+
+NTSTATUS
+DoBinderyLogon(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+ );
+
+NTSTATUS
+ConnectToServer(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PSCB *pScbCollision
+ );
+
+BOOLEAN
+ProcessFindNearestEntry(
+ PIRP_CONTEXT IrpContext,
+ PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
+ );
+
+NTSTATUS
+GetMaxPacketSize(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+PNONPAGED_SCB
+FindServer(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ PUNICODE_STRING ServerName
+ );
+
+NTSTATUS
+NwAllocateAndInitScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UidServerName OPTIONAL,
+ IN PUNICODE_STRING ServerName OPTIONAL,
+ OUT PSCB *ppScb
+);
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, ExtractNextComponentName )
+#pragma alloc_text( PAGE, ExtractPathAndFileName )
+#pragma alloc_text( PAGE, CrackPath )
+#pragma alloc_text( PAGE, CreateScb )
+#pragma alloc_text( PAGE, FindServer )
+#pragma alloc_text( PAGE, ProcessFindNearestEntry )
+#pragma alloc_text( PAGE, NegotiateBurstMode )
+#pragma alloc_text( PAGE, GetMaxPacketSize )
+#pragma alloc_text( PAGE, NwDeleteScb )
+#pragma alloc_text( PAGE, NwLogoffAndDisconnect )
+#pragma alloc_text( PAGE, InitializeAttach )
+#pragma alloc_text( PAGE, OpenScbSockets )
+#pragma alloc_text( PAGE, DoBinderyLogon )
+#pragma alloc_text( PAGE, QueryServersAddress )
+#pragma alloc_text( PAGE, TreeConnectScb )
+#pragma alloc_text( PAGE, TreeDisconnectScb )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, ProcessFindNearest )
+#pragma alloc_text( PAGE1, NwLogoffAllServers )
+#pragma alloc_text( PAGE1, DestroyAllScb )
+#pragma alloc_text( PAGE1, SelectConnection )
+#pragma alloc_text( PAGE1, NwFindScb )
+#pragma alloc_text( PAGE1, ConnectToServer )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+VOID
+ExtractNextComponentName (
+ OUT PUNICODE_STRING Name,
+ IN PUNICODE_STRING Path,
+ IN BOOLEAN ColonSeparator
+ )
+
+/*++
+
+Routine Description:
+
+ This routine extracts a the "next" component from a path string.
+
+ It assumes that
+
+Arguments:
+
+ Name - Returns a pointer to the component.
+
+ Path - Supplies a pointer to the backslash seperated pathname.
+
+ ColonSeparator - A colon can be used to terminate this component
+ name.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ register USHORT i; // Index into Name string.
+
+ PAGED_CODE();
+
+ if (Path->Length == 0) {
+ RtlInitUnicodeString(Name, NULL);
+ return;
+ }
+
+ //
+ // Initialize the extracted name to the name passed in skipping the
+ // leading backslash.
+ //
+
+ // DebugTrace(+0, Dbg, "NwExtractNextComponentName = %wZ\n", Path );
+
+ Name->Buffer = Path->Buffer + 1;
+ Name->Length = Path->Length - sizeof(WCHAR);
+ Name->MaximumLength = Path->MaximumLength - sizeof(WCHAR);
+
+ //
+ // Scan forward finding the terminal "\" in the server name.
+ //
+
+ for (i=0;i<(USHORT)(Name->Length/sizeof(WCHAR));i++) {
+
+ if ( Name->Buffer[i] == OBJ_NAME_PATH_SEPARATOR ||
+ ( ColonSeparator && Name->Buffer[i] == L':' ) ) {
+ break;
+ }
+ }
+
+ //
+ // Update the length and maximum length of the structure
+ // to match the new length.
+ //
+
+ Name->Length = Name->MaximumLength = (USHORT)(i*sizeof(WCHAR));
+}
+
+
+NTSTATUS
+ExtractPathAndFileName (
+ IN PUNICODE_STRING EntryPath,
+ OUT PUNICODE_STRING PathString,
+ OUT PUNICODE_STRING FileName
+ )
+/*++
+
+Routine Description:
+
+ This routine cracks the entry path into two pieces, the path and the file
+name component at the start of the name.
+
+
+Arguments:
+
+ IN PUNICODE_STRING EntryPath - Supplies the path to disect.
+ OUT PUNICODE_STRING PathString - Returns the directory containing the file.
+ OUT PUNICODE_STRING FileName - Returns the file name specified.
+
+Return Value:
+
+ NTSTATUS - SUCCESS
+
+
+--*/
+
+{
+ UNICODE_STRING Component;
+ UNICODE_STRING FilePath = *EntryPath;
+
+ PAGED_CODE();
+
+ // Strip trailing separators
+ while ( (FilePath.Length != 0) &&
+ FilePath.Buffer[(FilePath.Length-1)/sizeof(WCHAR)] ==
+ OBJ_NAME_PATH_SEPARATOR ) {
+
+ FilePath.Length -= sizeof(WCHAR);
+ FilePath.MaximumLength -= sizeof(WCHAR);
+ }
+
+ // PathString will become EntryPath minus FileName and trailing separators
+ *PathString = FilePath;
+
+ // Initialize FileName just incase there are no components at all.
+ RtlInitUnicodeString( FileName, NULL );
+
+ //
+ // Scan through the current file name to find the entire path
+ // up to (but not including) the last component in the path.
+ //
+
+ do {
+
+ //
+ // Extract the next component from the name.
+ //
+
+ ExtractNextComponentName(&Component, &FilePath, FALSE);
+
+ //
+ // Bump the "remaining name" pointer by the length of this
+ // component
+ //
+
+ if (Component.Length != 0) {
+
+ FilePath.Length -= Component.Length+sizeof(WCHAR);
+ FilePath.MaximumLength -= Component.MaximumLength+sizeof(WCHAR);
+ FilePath.Buffer += (Component.Length/sizeof(WCHAR))+1;
+
+ *FileName = Component;
+ }
+
+
+ } while (Component.Length != 0);
+
+ //
+ // Take the name, subtract the last component of the name
+ // and concatenate the current path with the new path.
+ //
+
+ if ( FileName->Length != 0 ) {
+
+ //
+ // Set the path's name based on the original name, subtracting
+ // the length of the name portion (including the "\")
+ //
+
+ PathString->Length -= (FileName->Length + sizeof(WCHAR));
+ if ( PathString->Length != 0 ) {
+ PathString->MaximumLength -= (FileName->MaximumLength + sizeof(WCHAR));
+ } else{
+ RtlInitUnicodeString( PathString, NULL );
+ }
+ } else {
+
+ // There was no path or filename
+
+ RtlInitUnicodeString( PathString, NULL );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CrackPath (
+ IN PUNICODE_STRING BaseName,
+ OUT PUNICODE_STRING DriveName,
+ OUT PWCHAR DriveLetter,
+ OUT PUNICODE_STRING ServerName,
+ OUT PUNICODE_STRING VolumeName,
+ OUT PUNICODE_STRING PathName,
+ OUT PUNICODE_STRING FileName,
+ OUT PUNICODE_STRING FullName OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine extracts the relevant portions from BaseName to extract
+ the components of the user's string.
+
+
+Arguments:
+
+ BaseName - Supplies the base user's path.
+
+ DriveName - Supplies a string to hold the drive specifier.
+
+ DriveLetter - Returns the drive letter. 0 for none, 'A'-'Z' for
+ disk drives, '1'-'9' for LPT connections.
+
+ ServerName - Supplies a string to hold the remote server name.
+
+ VolumeName - Supplies a string to hold the volume name.
+
+ PathName - Supplies a string to hold the remaining part of the path.
+
+ FileName - Supplies a string to hold the final component of the path.
+
+ FullName - Supplies a string to put the Path followed by FileName
+
+Return Value:
+
+ NTSTATUS - Status of operation
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ UNICODE_STRING BaseCopy = *BaseName;
+ UNICODE_STRING ShareName;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( DriveName, NULL);
+ RtlInitUnicodeString( ServerName, NULL);
+ RtlInitUnicodeString( VolumeName, NULL);
+ RtlInitUnicodeString( PathName, NULL);
+ RtlInitUnicodeString( FileName, NULL);
+ *DriveLetter = 0;
+
+ if (ARGUMENT_PRESENT(FullName)) {
+ RtlInitUnicodeString( FullName, NULL);
+ }
+
+ //
+ // If the name is "\", or empty, there is nothing to do.
+ //
+
+ if ( BaseName->Length <= sizeof( WCHAR ) ) {
+ return STATUS_SUCCESS;
+ }
+
+ ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+
+ if ((ServerName->Length == sizeof(L"X:") - sizeof(WCHAR) ) &&
+ (ServerName->Buffer[(ServerName->Length / sizeof(WCHAR)) - 1] == L':'))
+ {
+
+ //
+ // The file name is of the form x:\server\volume\foo\bar
+ //
+
+ *DriveName = *ServerName;
+ *DriveLetter = DriveName->Buffer[0];
+
+ RtlInitUnicodeString( ServerName, NULL );
+ ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+ }
+ }
+ else if ( ( ServerName->Length == sizeof(L"LPTx") - sizeof(WCHAR) ) &&
+ ( _wcsnicmp( ServerName->Buffer, L"LPT", 3 ) == 0) &&
+ ( ServerName->Buffer[3] >= '0' && ServerName->Buffer[3] <= '9' ) )
+ {
+
+ //
+ // The file name is of the form LPTx\server\printq
+ //
+
+ *DriveName = *ServerName;
+ *DriveLetter = DriveName->Buffer[3];
+
+ RtlInitUnicodeString( ServerName, NULL );
+ ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+ }
+ }
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // The file name is of the form \\server\volume\foo\bar
+ // Set volume name to server\volume.
+ //
+
+ ExtractNextComponentName( &ShareName, &BaseCopy, TRUE );
+
+ //
+ // Set volume name = \drive:\server\share or \server\share if the
+ // path is UNC.
+ //
+
+ VolumeName->Buffer = ServerName->Buffer - 1;
+
+ if ( ShareName.Length != 0 ) {
+
+ VolumeName->Length = ServerName->Length + ShareName.Length + 2 * sizeof( WCHAR );
+
+ if ( DriveName->Buffer != NULL ) {
+ VolumeName->Buffer = DriveName->Buffer - 1;
+ VolumeName->Length += DriveName->Length + sizeof(WCHAR);
+ }
+
+ BaseCopy.Buffer += ShareName.Length / sizeof(WCHAR) + 1;
+ BaseCopy.Length -= ShareName.Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ShareName.MaximumLength + sizeof(WCHAR);
+
+ } else {
+
+ VolumeName->Length = ServerName->Length + sizeof( WCHAR );
+ return( STATUS_SUCCESS );
+
+ }
+
+ VolumeName->MaximumLength = VolumeName->Length;
+ }
+ else
+ {
+ //
+ // server name is empty. this should only happen if we are
+ // opening the redirector itself. if there is volume or other
+ // components left, fail it.
+ //
+
+ if (BaseCopy.Length > sizeof(WCHAR))
+ {
+ return STATUS_BAD_NETWORK_PATH ;
+ }
+ }
+
+ Status = ExtractPathAndFileName ( &BaseCopy, PathName, FileName );
+
+ if (NT_SUCCESS(Status) &&
+ ARGUMENT_PRESENT(FullName)) {
+
+ //
+ // Use the feature that PathName and FileName are in the same buffer
+ // to return <pathname>\<filename>
+ //
+
+ if ( PathName->Buffer == NULL ) {
+
+ // return just <filename> or NULL
+
+ *FullName = *FileName;
+
+ } else {
+ // Set FullFileName to <PathName>'\'<FileName>
+
+ FullName->Buffer = PathName->Buffer;
+
+ FullName->Length = PathName->Length +
+ FileName->Length +
+ sizeof(WCHAR);
+
+ FullName->MaximumLength = PathName->MaximumLength +
+ FileName->MaximumLength +
+ sizeof(WCHAR);
+ }
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+GetServerByAddress(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PSCB *Scb,
+ IN IPXaddress *pServerAddress
+)
+/*+++
+
+Description:
+
+ This routine looks up a server by address. If it finds a server that
+ has been connected, it returns it referenced. Otherwise, it returns no
+ server.
+
+---*/
+{
+
+ NTSTATUS Status;
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pFirstNpScb, pNextNpScb;
+ PNONPAGED_SCB pFoundNpScb = NULL;
+ UNICODE_STRING CredentialName;
+
+ //
+ // Start at the head of the SCB list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ if ( ScbQueue.Flink == &ScbQueue ) {
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ ScbQueueEntry = ScbQueue.Flink;
+ pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+ pNextNpScb = pFirstNpScb;
+
+ //
+ // Leave the first SCB referenced since we need it to
+ // be there for when we walk all the way around the list.
+ //
+
+ NwReferenceScb( pFirstNpScb );
+ NwReferenceScb( pNextNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ while ( TRUE ) {
+
+ //
+ // Check to see if the SCB address matches the address we have
+ // and if the user uid matches the uid for this request. Skip
+ // matches that are abandoned anonymous creates.
+ //
+
+ if ( pNextNpScb->pScb ) {
+
+ if ( ( RtlCompareMemory( (BYTE *) pServerAddress,
+ (BYTE *) &pNextNpScb->ServerAddress,
+ IPX_HOST_ADDR_LEN ) == IPX_HOST_ADDR_LEN ) &&
+ ( pIrpContext->Specific.Create.UserUid.QuadPart ==
+ pNextNpScb->pScb->UserUid.QuadPart ) &&
+ ( pNextNpScb->State != SCB_STATE_FLAG_SHUTDOWN ) ) {
+
+ if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
+
+ //
+ // On a credential create, the credential supplied has
+ // to match the extended credential for the server.
+ //
+
+ Status = GetCredentialFromServerName( &pNextNpScb->ServerName,
+ &CredentialName );
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ContinueLoop;
+ }
+
+ if ( RtlCompareUnicodeString( &CredentialName,
+ pIrpContext->Specific.Create.puCredentialName,
+ TRUE ) ) {
+ goto ContinueLoop;
+ }
+
+ }
+
+ pFoundNpScb = pNextNpScb;
+ DebugTrace( 0, Dbg, "GetServerByAddress: %wZ\n", &pFoundNpScb->ServerName );
+ break;
+
+ }
+ }
+
+ContinueLoop:
+
+ //
+ // Otherwise, get the next one in the list. Don't
+ // forget to skip the list head.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ NwDereferenceScb( pNextNpScb );
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if ( pNextNpScb == pFirstNpScb ) {
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ break;
+ }
+
+ //
+ // Otherwise, reference this SCB and continue.
+ //
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ }
+
+ NwDereferenceScb( pFirstNpScb );
+
+ if ( pFoundNpScb ) {
+ *Scb = pFoundNpScb->pScb;
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_UNSUCCESSFUL;
+
+}
+
+NTSTATUS
+CheckScbSecurity(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN fDeferLogon
+)
+/*+++
+
+ You must be at the head of the queue to call this function.
+ This function makes sure that the Scb is valid for the user
+ that requested it.
+
+---*/
+{
+
+ NTSTATUS Status;
+ BOOLEAN SecurityConflict = FALSE;
+
+ ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ //
+ // If there's no user name or password, there's no conflict.
+ //
+
+ if ( ( puUserName == NULL ) &&
+ ( puPassword == NULL ) ) {
+
+ return STATUS_SUCCESS;
+ }
+
+ if ( pScb->UserName.Length &&
+ pScb->UserName.Buffer ) {
+
+ //
+ // Do a bindery security check if we were bindery
+ // authenticated to this server.
+ //
+
+ if ( !fDeferLogon &&
+ puUserName != NULL &&
+ puUserName->Buffer != NULL ) {
+
+ ASSERT( pScb->Password.Buffer != NULL );
+
+ if ( !RtlEqualUnicodeString( &pScb->UserName, puUserName, TRUE ) ||
+ ( puPassword &&
+ puPassword->Buffer &&
+ puPassword->Length &&
+ !RtlEqualUnicodeString( &pScb->Password, puPassword, TRUE ) )) {
+
+ SecurityConflict = TRUE;
+
+ }
+ }
+
+ } else {
+
+ //
+ // Do an nds security check.
+ //
+
+ Status = NdsCheckCredentials( pIrpContext,
+ puUserName,
+ puPassword );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ SecurityConflict = TRUE;
+ }
+
+ }
+
+ //
+ // If there was a security conflict, see if we can just
+ // take this connection over (i.e. there are no open
+ // files or open handles to the server).
+ //
+
+ if ( SecurityConflict ) {
+
+ if ( ( pScb->OpenFileCount == 0 ) &&
+ ( pScb->IcbCount == 0 ) ) {
+
+ if ( pScb->UserName.Buffer ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ }
+
+ RtlInitUnicodeString( &pScb->UserName, NULL );
+ RtlInitUnicodeString( &pScb->Password, NULL );
+ pScb->pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "SCB security conflict.\n", 0 );
+ return STATUS_NETWORK_CREDENTIAL_CONFLICT;
+
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "SCB security check succeeded.\n", 0 );
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+GetScb(
+ OUT PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ OUT PBOOLEAN Existing
+)
+/*+++
+
+Description:
+
+ This routine locates an existing SCB or creates a new SCB.
+ This is the first half of the original CreateScb routine.
+
+Locks:
+
+ See the anonymous create information in CreateScb().
+
+---*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+ BOOLEAN ExistingScb = TRUE;
+ UNICODE_STRING UidServer;
+ UNICODE_STRING ExCredName;
+ PUNICODE_STRING puConnectName;
+ KIRQL OldIrql;
+
+ DebugTrace( 0, Dbg, "GetScb... %wZ\n", Server );
+
+ if ( pServerAddress != NULL ) {
+ DebugTrace( 0, Dbg, " ->Server Address = (provided)\n", 0 );
+ } else {
+ DebugTrace( 0, Dbg, " ->Server Address = NULL\n", 0 );
+ }
+
+ RtlInitUnicodeString( &UidServer, NULL );
+
+ if ( ( Server == NULL ) ||
+ ( Server->Length == 0 ) ) {
+
+ //
+ // No server name was provided. Either this is a connect by address,
+ // or a connect to a nearby bindery server (defaulting to the preferred
+ // server).
+ //
+
+ if ( pServerAddress == NULL ) {
+
+ //
+ // No server address was provided, so this is an attempt to open
+ // a nearby bindery server.
+ //
+
+ while (TRUE) {
+
+ //
+ // The loop checks that after we get to the front, the SCB
+ // is still in the state we wanted. If not, we need to
+ // reselect another.
+ //
+
+ pNpScb = SelectConnection( NULL );
+
+ //
+ // Note: We'd like to call SelectConnection with the pNpScb
+ // that we last tried, but if the scavenger runs before
+ // this loop gets back to the select connection, we could
+ // pass a bum pointer to SelectConnection, which is bad.
+ //
+
+ if ( pNpScb != NULL) {
+
+ pScb = pNpScb->pScb;
+
+ //
+ // Queue ourselves to the SCB, wait to get to the front to
+ // protect access to server State.
+ //
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ //
+ // These states have to match the conditions of the
+ // SelectConnection to prevent an infinite loop.
+ //
+
+ if (!((pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
+ (pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
+ (pNpScb->State == SCB_STATE_IN_USE ))) {
+
+ //
+ // No good any more as default server, select another.
+ //
+
+ pScb = NULL ;
+ NwDereferenceScb( pNpScb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ continue ;
+
+ }
+ }
+
+ //
+ // otherwise, we're done
+ //
+
+ break ;
+
+ }
+
+ } else {
+
+ //
+ // An address was provided, so we are attempting to do a lookup
+ // based on address. The server that we are looking for might
+ // exist but not yet have its address recorded, so if we do an
+ // anonymous create, we have to check at the end whether or not
+ // someone else came in and successfully created while we were
+ // looking up the name.
+ //
+ // We don't have to hold the RCB anymore since colliding creates
+ // have to be handled gracefully anyway.
+ //
+
+ Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // No anonymous creates are allowed if we are not allowed
+ // to send packets to the net (because it's not possible for
+ // us to resolve the address to a name).
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // There's no connection to this server, so we'll
+ // have to create one. Let's start with an anonymous
+ // Scb.
+ //
+
+ Status = NwAllocateAndInitScb( pIrpContext,
+ NULL,
+ NULL,
+ &pScb );
+
+ if ( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ //
+ // We've made the anonymous create, so put it on the scb
+ // list and get to the head of the queue.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pScb->pNpScb;
+
+ ExInterlockedInsertHeadList( &pScb->pNpScb->Requests,
+ &pIrpContext->NextRequest,
+ &pScb->pNpScb->NpScbSpinLock );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+ InsertTailList(&ScbQueue, &pScb->pNpScb->ScbLinks);
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ DebugTrace( 0, Dbg, "GetScb started an anonymous create.\n", 0 );
+ ExistingScb = FALSE;
+
+ } else {
+
+ //
+ // Get to the head of the queue and see if this was
+ // an abandoned anonymous create. If so, get the
+ // right server and continue.
+ //
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pScb->pNpScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ if ( pScb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) {
+
+ //
+ // The create abandoned this scb, redoing a
+ // GetServerByAddress() is guaranteed to get
+ // us a good server if there is a server out
+ // there.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pScb->pNpScb );
+
+ Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
+
+ if ( NT_SUCCESS( Status ) ) {
+ ASSERT( pScb != NULL );
+ ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
+ }
+
+ } else {
+
+ ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
+ }
+ }
+
+ ASSERT( pScb != NULL );
+ }
+
+ } else {
+
+ //
+ // A server name was provided, so we are doing a straight
+ // lookup or create by name. Do we need to munge the name
+ // for a supplemental credential connect?
+ //
+
+ RtlInitUnicodeString( &ExCredName, NULL );
+
+ if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
+ ( !IsCredentialName( Server ) ) ) {
+
+ Status = BuildExCredentialServerName( Server,
+ pIrpContext->Specific.Create.puCredentialName,
+ &ExCredName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ puConnectName = &ExCredName;
+
+ } else {
+
+ puConnectName = Server;
+ }
+
+ Status = MakeUidServer( &UidServer,
+ &pIrpContext->Specific.Create.UserUid,
+ puConnectName );
+
+
+ if ( ExCredName.Buffer ) {
+ FREE_POOL( ExCredName.Buffer );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = %wZ\n", &UidServer );
+
+ ExistingScb = NwFindScb( &pScb, pIrpContext, &UidServer, Server );
+
+ ASSERT( pScb != NULL );
+ pNpScb = pScb->pNpScb;
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+ NwAppendToQueueAndWait(pIrpContext);
+
+ if ( ExistingScb ) {
+
+ //
+ // We found an existing SCB. If we are logged into this
+ // server make sure the supplied username and password, match
+ // the username and password that we logged in with.
+ //
+
+ if ( pNpScb->State == SCB_STATE_IN_USE ) {
+
+ Status = CheckScbSecurity( pIrpContext,
+ pScb,
+ UserName,
+ Password,
+ DeferLogon );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( UidServer.Buffer );
+ UidServer.Buffer = NULL;
+ NwDereferenceScb( pNpScb );
+ return Status;
+ }
+ }
+ }
+
+ }
+
+ //
+ // 1) We may or may not have a server (evidenced by pScb).
+ //
+ // 2) If we have a server and ExistingScb is TRUE, we have
+ // an existing server, possibly already connected.
+ // Otherwise, we have a newly created server that
+ // may or may not be anonymous.
+ //
+
+ *Scb = pScb;
+ *Existing = ExistingScb;
+
+#ifdef NWDBG
+
+ if ( pScb != NULL ) {
+
+ //
+ // If we have a server, the SCB is referenced and we will
+ // be at the head of the queue.
+ //
+
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+
+ }
+
+#endif
+
+ if ( UidServer.Buffer != NULL ) {
+ FREE_POOL( UidServer.Buffer );
+ }
+
+ DebugTrace( 0, Dbg, "GetScb returned %08lx\n", pScb );
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+ConnectScb(
+ IN PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection,
+ IN BOOLEAN ExistingScb
+)
+/*+++
+
+Description:
+
+ This routine puts the provided scb in the connected state.
+ This is the second half of the original CreateScb routine.
+
+Arguments:
+
+ Scb - The scb for the server we want to connect.
+ pIrpContext - The context for this request.
+ Server - The name of the server, or NULL.
+ pServerAddress - The address of the server, or NULL,
+ UserName - The name of the user to connect as, or NULL.
+ Password - The password for the user, or NULL.
+ DeferLogon - Should we defer the logon?
+ DeleteConnection - Should we succeed even without the net so that
+ the delete request will succeed?
+ ExistingScb - Is this an existing SCB?
+
+ If the SCB is anonymous, we need to safely check for colliding
+ creates when we find out who the server is.
+
+ If this is a reconnect attempt, this routine will not dequeue the
+ irp context, which could cause a deadlock in the reconnect logic.
+
+---*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PSCB pScb = *Scb;
+ PNONPAGED_SCB pNpScb = NULL;
+
+ BOOLEAN AnonymousScb = FALSE;
+ PSCB pCollisionScb = NULL;
+
+ NTSTATUS LoginStatus;
+ BOOLEAN TriedNdsLogin;
+
+ PLOGON pLogon;
+ BOOLEAN DeferredLogon = DeferLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ NTSTATUS CredStatus;
+
+ DebugTrace( 0, Dbg, "ConnectScb... %08lx\n", pScb );
+
+ //
+ // If we already have an SCB, find out where in the
+ // connect chain we need to start off.
+ //
+
+ if ( pScb ) {
+
+ pNpScb = pScb->pNpScb;
+ AnonymousScb = IS_ANONYMOUS_SCB( pScb );
+
+ if ( ExistingScb ) {
+
+ ASSERT( !AnonymousScb );
+
+ //
+ // If this SCB is in STATE_ATTACHING, we need to check
+ // the address in the SCB to make sure that it was at one
+ // point a valid server. If it wasn't, then we shouldn't
+ // honor this create because it's probably a tree create.
+ //
+
+ if ( DeleteConnection ) {
+
+ ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) );
+
+ if ( ( pNpScb->State == SCB_STATE_ATTACHING ) &&
+ ( (pNpScb->ServerAddress).Socket == 0 ) ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto CleanupAndExit;
+
+ } else {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_SUCCESS;
+ }
+ }
+
+RedoConnect:
+
+ if ( pNpScb->State == SCB_STATE_ATTACHING ) {
+ goto GetAddress;
+ } else if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+ goto Connect;
+ } else if ( pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+ goto Login;
+ } else if ( pNpScb->State == SCB_STATE_IN_USE ) {
+ goto InUse;
+ } else {
+
+ DebugTrace( 0, Dbg, "ConnectScb: Unknown Scb State %08lx\n", pNpScb->State );
+ Status = STATUS_INVALID_PARAMETER;
+ goto CleanupAndExit;
+ }
+
+ } else {
+
+ //
+ // This is a new SCB, we have to run through the whole routine.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ }
+
+GetAddress:
+
+ //
+ // Set the reroute attempted bit so that we don't try
+ // to reconnect during the connect.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( !pServerAddress ) {
+
+ //
+ // If we don't have an address, this SCB cannot be anonymous!!
+ //
+
+ ASSERT( !AnonymousScb );
+
+ //
+ // We have to cast an exception frame for this legacy routine
+ // that still uses structured exceptions.
+ //
+
+ try {
+
+ pNpScb = FindServer( pIrpContext, pNpScb, Server );
+
+ ASSERT( pNpScb != NULL );
+
+ //
+ // This is redundant unless the starting server was NULL.
+ // FindServer returns the same SCB we provided to it
+ // unless we called it with NULL.
+ //
+
+ pScb = pNpScb->pScb;
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ goto CleanupAndExit;
+ }
+
+ } else {
+
+ //
+ // Build the address into the NpScb since we already know it.
+ //
+
+ RtlCopyMemory( &pNpScb->ServerAddress,
+ pServerAddress,
+ sizeof( TDI_ADDRESS_IPX ) );
+
+ BuildIpxAddress( pNpScb->ServerAddress.Net,
+ pNpScb->ServerAddress.Node,
+ NCP_SOCKET,
+ &pNpScb->RemoteAddress );
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ }
+
+Connect:
+
+ //
+ // FindServer may have connected us to the server already,
+ // so we may be able to skip the reconnect here.
+ //
+
+ if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+
+ //
+ // If this is an anonymous scb, we have to be prepared
+ // for ConnectToServer() to find that we've already connected
+ // this server by name. In this case, we cancel the
+ // anonymous create and use the server that was created
+ // while we were looking up the name.
+ //
+
+ Status = ConnectToServer( pIrpContext, &pCollisionScb );
+
+ if (!NT_SUCCESS(Status)) {
+ goto CleanupAndExit;
+ }
+
+ //
+ // We succeeded. If there's a collision scb, then we need to
+ // abandon the anonymous scb and use the scb that we collided
+ // with. Otherwise, we successfully completed an anonymous
+ // connect and can go on with the create normally.
+ //
+
+ if ( pCollisionScb ) {
+
+ ASSERT( AnonymousScb );
+
+ //
+ // Deref and dequeue from the abandoned server.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pNpScb );
+
+ //
+ // Queue to the appropriate server.
+ //
+
+ pIrpContext->pScb = pCollisionScb;
+ pIrpContext->pNpScb = pCollisionScb->pNpScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ pScb = pCollisionScb;
+ pNpScb = pCollisionScb->pNpScb;
+ *Scb = pCollisionScb;
+
+ //
+ // Re-start connecting the scb.
+ //
+
+ AnonymousScb = FALSE;
+ ExistingScb = TRUE;
+
+ pCollisionScb = NULL;
+
+ DebugTrace( 0, Dbg, "Re-doing connect on anonymous collision.\n", 0 );
+ goto RedoConnect;
+
+ }
+
+ DebugTrace( +0, Dbg, " Logout from server - just in case\n", 0);
+
+ Status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F",
+ NCP_LOGOUT );
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto CleanupAndExit;
+ }
+
+ DebugTrace( +0, Dbg, " Connect to real server = %X\n", Status);
+
+ pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+ }
+
+Login:
+
+ //
+ // If we have credentials for the tree and this server was named
+ // explicitly, we shouldn't defer the login or else the browse
+ // view of the tree may be wrong. For this reason, NdsServerAuthenticate
+ // has to be a straight shot call and can't remove us from the head
+ // of the queue.
+ //
+
+ if ( ( ( Server != NULL ) || ( pServerAddress != NULL ) ) &&
+ ( DeferredLogon ) &&
+ ( pScb->MajorVersion > 3 ) &&
+ ( pScb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ CredStatus = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( CredStatus ) ) {
+
+ if ( ( pNdsContext->Credential != NULL ) &&
+ ( pNdsContext->CredentialLocked == FALSE ) ) {
+
+ DebugTrace( 0, Dbg, "Forcing authentication to %wZ.\n",
+ &pScb->UidServerName );
+ DeferredLogon = FALSE;
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+ }
+ }
+
+ if (pNpScb->State == SCB_STATE_LOGIN_REQUIRED && !DeferredLogon ) {
+
+ //
+ // NOTE: DoBinderyLogon() and DoNdsLogon() may return a
+ // warning status. If they do, we must return the
+ // warning status to the caller.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+ TriedNdsLogin = FALSE;
+
+ //
+ // We force a bindery login for a non 4.x server. Otherwise, we
+ // allow a fall-back from NDS style authentication to bindery style
+ // authentication.
+ //
+
+ if ( pScb->MajorVersion >= 4 ) {
+
+ ASSERT( pScb->NdsTreeName.Length != 0 );
+
+ Status = DoNdsLogon( pIrpContext, UserName, Password );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Do we need to re-license the connection?
+ //
+
+ if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ }
+ }
+
+ }
+
+ TriedNdsLogin = TRUE;
+ LoginStatus = Status;
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ Status = DoBinderyLogon( pIrpContext, UserName, Password );
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( TriedNdsLogin ) {
+
+ //
+ // Both login attempts have failed. We usually prefer
+ // the NDS status, but not always.
+ //
+
+ if ( ( Status != STATUS_WRONG_PASSWORD ) &&
+ ( Status != STATUS_ACCOUNT_DISABLED ) ) {
+ Status = LoginStatus;
+ }
+ }
+
+ //
+ // Couldn't log on, be good boys and disconnect.
+ //
+
+ ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "D-" ); // Disconnect
+
+ Stats.Sessions--;
+
+ if ( pScb->MajorVersion == 2 ) {
+ Stats.NW2xConnects--;
+ } else if ( pScb->MajorVersion == 3 ) {
+ Stats.NW3xConnects--;
+ } else if ( pScb->MajorVersion == 4 ) {
+ Stats.NW4xConnects--;
+ }
+
+ //
+ // Demote this scb to reconnect required and exit.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ goto CleanupAndExit;
+ }
+
+ pNpScb->State = SCB_STATE_IN_USE;
+ }
+
+ //
+ // We have to be at the head of the queue to do the reconnect.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+ } else {
+ NwAppendToQueueAndWait( pIrpContext );
+ }
+
+ ReconnectScb( pIrpContext, pScb );
+
+InUse:
+
+ //
+ // Ok, we've completed the connect routine. Return this good server.
+ //
+
+ *Scb = pScb;
+
+CleanupAndExit:
+
+ //
+ // The reconnect path must not do anything to remove the irp context from
+ // the head of the queue since it also owns the irp context in the second
+ // position on the queue and that irp context is running.
+ //
+
+ if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ DebugTrace( 0, Dbg, "ConnectScb: Connected %08lx\n", pScb );
+ DebugTrace( 0, Dbg, "ConnectScb: Status was %08lx\n", Status );
+ return Status;
+
+}
+
+NTSTATUS
+CreateScb(
+ OUT PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection
+)
+/*++
+
+Routine Description:
+
+ This routine connects to the requested server.
+
+ The following mix of parameters are valid:
+
+ Server Name, No Net Address - The routine will look up
+ up the SCB or create a new one if necessary, getting
+ the server address from a nearby bindery.
+
+ No Server Name, Valid Net Address - The routine will
+ look up the SCB by address or create a new one if
+ necessary. The name of the server will be set in
+ the SCB upon return.
+
+ Server Name, Valid Net Address - The routine will look
+ up the SCB by name or will create a new one if
+ necessary. The supplied server address will be used,
+ sparing a bindery query.
+
+ No Server Name, No Net Address - A connection to the
+ preferred server or a nearby server will be returned.
+
+Arguments:
+
+ Scb - The pointer to the scb in question.
+ pIrpContext - The information for this request.
+ Server - The name of the server, or NULL.
+ pServerAddress - The address of the server, or NULL.
+ UserName - The username for the connect, or NULL.
+ Password - The password for the connect, or NULL.
+ DeferLogon - Should we defer the logon until later?
+ DeleteConnection - Should we allow this even when there's no
+ net response so that the connection can
+ be deleted?
+
+Return Value:
+
+ NTSTATUS - Status of operation. If the return status is STATUS_SUCCESS,
+ then Scb must point to a valid Scb. The irp context pointers will also
+ be set, but the irp context will not be on the scb queue.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pOriginalNpScb = pIrpContext->pNpScb;
+ PSCB pOriginalScb = pIrpContext->pScb;
+ BOOLEAN ExistingScb = FALSE;
+ BOOLEAN AnonymousScb = FALSE;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CreateScb....\n", 0);
+
+ //
+ // Do not allow any SCB opens unless the redirector is running
+ // unless they are no connect creates and we are waiting to bind.
+ //
+
+ if ( NwRcb.State != RCB_STATE_RUNNING ) {
+
+ if ( ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ||
+ ( NwRcb.State != RCB_STATE_NEED_BIND ) ) ) {
+
+ *Scb = NULL;
+ DebugTrace( -1, Dbg, "CreateScb -> %08lx\n", STATUS_REDIRECTOR_NOT_STARTED );
+ return STATUS_REDIRECTOR_NOT_STARTED;
+ }
+ }
+
+ if ( UserName != NULL ) {
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", UserName );
+ } else {
+ DebugTrace( 0, Dbg, " ->UserName = NULL\n", 0 );
+ }
+
+ if ( Password != NULL ) {
+ DebugTrace( 0, Dbg, " ->Password = %wZ\n", Password );
+ } else {
+ DebugTrace( 0, Dbg, " ->Password = NULL\n", 0 );
+ }
+
+ //
+ // Get the SCB for this server.
+ //
+
+ Status = GetScb( &pScb,
+ pIrpContext,
+ Server,
+ pServerAddress,
+ UserName,
+ Password,
+ DeferLogon,
+ &ExistingScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // At this point, we may or may not have an SCB.
+ //
+ // If we have an SCB, we know:
+ //
+ // 1. The scb is referenced.
+ // 2. We are at the head of the queue.
+ //
+ // IMPORTANT POINT: The SCB may be anonymous. If it is,
+ // we do not hold the RCB, but rather we have to re-check
+ // whether or not the server has shown up via a different
+ // create when we find out who the anonymous server is.
+ // We do this because there is a window where we have a
+ // servers name but not its address and so our lookup by
+ // address might be inaccurate.
+ //
+
+ if ( ( pScb ) && IS_ANONYMOUS_SCB( pScb ) ) {
+ AnonymousScb = TRUE;
+ }
+
+ //
+ // If we have a fully connected SCB, we need to go no further.
+ //
+
+ if ( ( pScb ) && ( pScb->pNpScb->State == SCB_STATE_IN_USE ) ) {
+
+ ASSERT( !AnonymousScb );
+
+ if ( ( pScb->MajorVersion >= 4 ) &&
+ ( pScb->UserName.Buffer == NULL ) ) {
+
+ //
+ // This is an NDS authenticated server and we have
+ // to make sure the credentials aren't locked for
+ // logout.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if ( ( pNdsContext->Credential != NULL ) &&
+ ( pNdsContext->CredentialLocked == TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Denying create... we're logging out.\n", 0 );
+ Status = STATUS_DEVICE_BUSY;
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+ }
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // We must not change the irp context pointers if we're going
+ // to fail this call or we may screw up ref counts and what not.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ *Scb = pScb;
+
+ } else {
+
+ *Scb = NULL;
+ NwDereferenceScb( pScb->pNpScb );
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalScb;
+
+ }
+
+
+ DebugTrace( -1, Dbg, "CreateScb: pScb = %08lx\n", pScb );
+ return Status;
+ }
+
+ //
+ // Run through the connect routines for this scb. The scb may
+ // be NULL if we're still looking for a nearby server.
+ //
+
+ Status = ConnectScb( &pScb,
+ pIrpContext,
+ Server,
+ pServerAddress,
+ UserName,
+ Password,
+ DeferLogon,
+ DeleteConnection,
+ ExistingScb );
+
+ //
+ // If ConnectScb fails, remove the extra ref count so
+ // the scavenger will clean it up. Anonymous failures
+ // are also cleaned up by the scavenger.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( pScb ) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ //
+ // We must not change the irp context pointers if we're going
+ // to fail this call or we may screw up ref counts and what not.
+ //
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalScb;
+ *Scb = NULL;
+
+ DebugTrace( -1, Dbg, "CreateScb: Status = %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // If ConnectScb succeeds, then we must have an scb, the scb must
+ // be in the IN_USE state (or LOGIN_REQUIRED if DeferLogon was
+ // specified), it must be referenced, and we should not be on the
+ // queue.
+ //
+
+ ASSERT( pScb );
+ ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
+ ASSERT( pIrpContext->pNpScb == pScb->pNpScb );
+ ASSERT( pIrpContext->pScb == pScb );
+ ASSERT( pScb->pNpScb->Reference > 0 );
+
+ *Scb = pScb;
+ DebugTrace(-1, Dbg, "CreateScb -> pScb = %08lx\n", pScb );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ return Status;
+}
+
+PNONPAGED_SCB
+FindServer(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ PUNICODE_STRING ServerName
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to get the network address of a server. If no
+ servers are known, it first sends a find nearest SAP.
+
+Arguments:
+
+ pIrpContext - A pointer to the request parameters.
+
+ pNpScb - A pointer to the non paged SCB for the server to get the
+ address of.
+
+Return Value:
+
+ NONPAGED_SCB - A pointer the nonpaged SCB. This is the same as the
+ input value, unless the input SCB was NULL. Then this is a
+ pointer to the nearest server SCB.
+
+ This routine raises status if it fails to get the server's address.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Attempts;
+ BOOLEAN FoundServer = FALSE;
+ PNONPAGED_SCB pNearestNpScb = NULL;
+ PNONPAGED_SCB pLastNpScb = NULL;
+
+ BOOLEAN SentFindNearest = FALSE;
+ BOOLEAN SentGeneral = FALSE;
+ PMDL ReceiveMdl = NULL;
+ PUCHAR ReceiveBuffer = NULL;
+ IPXaddress ServerAddress;
+
+ BOOLEAN ConnectedToNearest = FALSE;
+ BOOLEAN AllocatedIrpContext = FALSE;
+ PIRP_CONTEXT pNewIrpContext;
+ int ResponseCount;
+ int NewServers;
+
+ static LARGE_INTEGER TimeoutWait = {0,0};
+ LARGE_INTEGER Now;
+
+ PAGED_CODE();
+
+ //
+ // If we had a SAP timeout less than 10 seconds ago, just fail this
+ // request immediately. This allows dumb apps to exit a lot faster.
+ //
+
+ KeQuerySystemTime( &Now );
+ if ( Now.QuadPart < TimeoutWait.QuadPart ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ }
+
+ try {
+ for ( Attempts = 0; Attempts < MAX_SAP_RETRIES && !FoundServer ; Attempts++ ) {
+
+ //
+ // If this SCB is now marked RECONNECT_REQUIRED, then
+ // it responded to the find nearest and we can immediately
+ // try to connect to it.
+ //
+
+ if ( pNpScb != NULL &&
+ pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+
+ return pNpScb;
+ }
+
+ //
+ // Pick a server to use to find the address of the server that
+ // we are really interested in.
+ //
+
+ if (pLastNpScb) {
+
+ //
+ // For some reason we couldn't use pNearestScb. Scan from this
+ // server onwards.
+ //
+
+ pNearestNpScb = SelectConnection( pLastNpScb );
+
+ // Allow pLastNpScb to be deleted.
+
+ NwDereferenceScb( pLastNpScb );
+
+ pLastNpScb = NULL;
+
+ } else {
+
+ pNearestNpScb = SelectConnection( NULL );
+
+ }
+
+ if ( pNearestNpScb == NULL ) {
+
+ int i;
+
+ //
+ // If we sent a find nearest, and still don't have a single
+ // entry in the server list, it's time to give up.
+ //
+
+ if (( SentFindNearest) &&
+ ( SentGeneral )) {
+
+ Error(
+ EVENT_NWRDR_NO_SERVER_ON_NETWORK,
+ STATUS_OBJECT_NAME_NOT_FOUND,
+ NULL,
+ 0,
+ 0 );
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ return NULL;
+ }
+
+ //
+ // We don't have any active servers in the list. Queue our
+ // IrpContext to the NwPermanentNpScb. This insures that
+ // only one thread in the system in doing a find nearest at
+ // any one time.
+ //
+
+ DebugTrace( +0, Dbg, " Nearest Server\n", 0);
+
+ if ( !AllocatedIrpContext ) {
+ AllocatedIrpContext = NwAllocateExtraIrpContext(
+ &pNewIrpContext,
+ &NwPermanentNpScb );
+
+ if ( !AllocatedIrpContext ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+ }
+
+ pNewIrpContext->pNpScb = &NwPermanentNpScb;
+
+ //
+ // Allocate an extra buffer large enough for 4
+ // find nearest responses, or a general SAP response.
+ //
+
+ pNewIrpContext->Specific.Create.FindNearestResponseCount = 0;
+ NewServers = 0;
+
+
+ ReceiveBuffer = ALLOCATE_POOL_EX(
+ NonPagedPool,
+ MAX_SAP_RESPONSE_SIZE );
+
+ pNewIrpContext->Specific.Create.FindNearestResponse[0] = ReceiveBuffer;
+
+ for ( i = 1; i < MAX_SAP_RESPONSES ; i++ ) {
+ pNewIrpContext->Specific.Create.FindNearestResponse[i] =
+ ReceiveBuffer + i * SAP_RECORD_SIZE;
+ }
+
+ //
+ // Get the tick count for this net, so that we know how
+ // long to wait for SAP responses.
+ //
+
+ (VOID)GetTickCount( pNewIrpContext, &NwPermanentNpScb.TickCount );
+ NwPermanentNpScb.SendTimeout = NwPermanentNpScb.TickCount + 10;
+
+ if (!SentFindNearest) {
+
+ //
+ // Send a find nearest SAP, and wait for up to several
+ // responses. This allows us to handle a busy server
+ // that responds quickly to SAPs but will not accept
+ // connections.
+ //
+
+ Status = ExchangeWithWait (
+ pNewIrpContext,
+ ProcessFindNearest,
+ "Aww",
+ SAP_FIND_NEAREST,
+ SAP_SERVICE_TYPE_SERVER );
+
+ if ( Status == STATUS_NETWORK_UNREACHABLE ) {
+
+ //
+ // IPX is not bound to anything that is currently
+ // up (which means it's probably bound only to the
+ // RAS WAN wrapper). Don't waste 20 seconds trying
+ // to find a server.
+ //
+
+ DebugTrace( 0, Dbg, "Aborting FindNearest. No Net.\n", 0 );
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ ExRaiseStatus( STATUS_NETWORK_UNREACHABLE );
+ }
+
+ //
+ // Process the set of find nearest responses.
+ //
+
+ for (i = 0; i < (int)pNewIrpContext->Specific.Create.FindNearestResponseCount; i++ ) {
+ if (ProcessFindNearestEntry(
+ pNewIrpContext,
+ (PSAP_FIND_NEAREST_RESPONSE)pNewIrpContext->Specific.Create.FindNearestResponse[i] )
+ ) {
+
+ //
+ // We found a server that was previously unknown.
+ //
+
+ NewServers++;
+ }
+ }
+ }
+
+ if (( !NewServers ) &&
+ ( !SentGeneral)){
+
+ SentGeneral = TRUE;
+
+ //
+ // Either no SAP responses or can't connect to nearest servers.
+ // Try a general SAP.
+ //
+
+ ReceiveMdl = ALLOCATE_MDL(
+ ReceiveBuffer,
+ MAX_SAP_RESPONSE_SIZE,
+ TRUE,
+ FALSE,
+ NULL );
+
+ if ( ReceiveMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( ReceiveMdl );
+ pNewIrpContext->RxMdl->Next = ReceiveMdl;
+
+ Status = ExchangeWithWait (
+ pNewIrpContext,
+ SynchronousResponseCallback,
+ "Aww",
+ SAP_GENERAL_REQUEST,
+ SAP_SERVICE_TYPE_SERVER );
+
+ if ( NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Received %d bytes\n", pNewIrpContext->ResponseLength );
+ ResponseCount = ( pNewIrpContext->ResponseLength - 2 ) / SAP_RECORD_SIZE;
+
+ //
+ // Process at most MAX_SAP_RESPONSES servers.
+ //
+
+ if ( ResponseCount > MAX_SAP_RESPONSES ) {
+ ResponseCount = MAX_SAP_RESPONSES;
+ }
+
+ for ( i = 0; i < ResponseCount; i++ ) {
+ ProcessFindNearestEntry(
+ pNewIrpContext,
+ (PSAP_FIND_NEAREST_RESPONSE)(pNewIrpContext->rsp + SAP_RECORD_SIZE * i) );
+ }
+ }
+
+ pNewIrpContext->RxMdl->Next = NULL;
+ FREE_MDL( ReceiveMdl );
+ ReceiveMdl = NULL;
+ }
+
+ //
+ // We're done with the find nearest. Free the buffer and
+ // dequeue from the permanent SCB.
+ //
+
+ FREE_POOL( ReceiveBuffer );
+ ReceiveBuffer = NULL;
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ if ( !NT_SUCCESS( Status ) &&
+ pNewIrpContext->Specific.Create.FindNearestResponseCount == 0 ) {
+
+ //
+ // If the SAP timed out, map the error for MPR.
+ //
+
+ if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // Setup the WaitTimeout, and fail this request.
+ //
+
+ KeQuerySystemTime( &TimeoutWait );
+ TimeoutWait.QuadPart += NwOneSecond * 10;
+
+ ExRaiseStatus( Status );
+ return NULL;
+ }
+
+ SentFindNearest = TRUE;
+
+ } else {
+
+ if ( !AllocatedIrpContext ) {
+ AllocatedIrpContext = NwAllocateExtraIrpContext(
+ &pNewIrpContext,
+ pNearestNpScb );
+
+ if ( !AllocatedIrpContext ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+ }
+
+ //
+ // Point the IRP context at the nearest server.
+ //
+
+ pNewIrpContext->pNpScb = pNearestNpScb;
+ NwAppendToQueueAndWait( pNewIrpContext );
+
+ if ( pNearestNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+
+ //
+ // We have no connection to this server, try to
+ // connect now. This is not a valid path for an
+ // anonymous create, so there's no chance that
+ // there will be a name collision.
+ //
+
+ Status = ConnectToServer( pNewIrpContext, NULL );
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Failed to connect to the server. Give up.
+ // We'll try another server.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ // Keep pNearestScb referenced
+ // so it doesn't disappear.
+
+ pLastNpScb = pNearestNpScb;
+
+ continue;
+
+ } else {
+
+ pNearestNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+ ConnectedToNearest = TRUE;
+
+ }
+ }
+
+ //
+ // update the last used time for this SCB.
+ //
+
+ KeQuerySystemTime( &pNearestNpScb->LastUsedTime );
+
+ if (( pNpScb == NULL ) ||
+ ( ServerName == NULL )) {
+
+ //
+ // We're looking for any server so use this one.
+ //
+ // We'll exit the for loop on the SCB queue,
+ // and with this SCB referenced.
+ //
+
+ pNpScb = pNearestNpScb;
+ Status = STATUS_SUCCESS;
+ FoundServer = TRUE;
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ } else {
+
+ Status = QueryServersAddress(
+ pNewIrpContext,
+ pNearestNpScb,
+ ServerName,
+ &ServerAddress );
+
+ //
+ // If we connect to this server just to query it's
+ // bindery, disconnect now.
+ //
+
+ if ( ConnectedToNearest && NT_SUCCESS(Status) ) {
+ ExchangeWithWait (
+ pNewIrpContext,
+ SynchronousResponseCallback,
+ "D-" ); // Disconnect
+
+ pNearestNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Success!
+ //
+ // Point the SCB at the real server address and connect to it,
+ // then logout. (We logout for no apparent reason except
+ // because this is what a netware redir does.)
+ //
+
+ RtlCopyMemory(
+ &pNpScb->ServerAddress,
+ &ServerAddress,
+ sizeof( TDI_ADDRESS_IPX ) );
+
+ BuildIpxAddress(
+ ServerAddress.Net,
+ ServerAddress.Node,
+ NCP_SOCKET,
+ &pNpScb->RemoteAddress );
+
+ FoundServer = TRUE;
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwDereferenceScb( pNearestNpScb );
+
+ pNewIrpContext->pNpScb = pNpScb;
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ } else {
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
+
+ //
+ // This server is no longer talking to us.
+ // Try again. Keep pNearestScb referenced
+ // so it doesn't disappear.
+ //
+
+ pLastNpScb = pNearestNpScb;
+
+ continue;
+
+ } else {
+
+ NwDereferenceScb( pNearestNpScb );
+
+ //
+ // This nearest server doesn't know about
+ // the server we are looking for. Give up
+ // and let another rdr try.
+ //
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ return NULL;
+ }
+ }
+ }
+
+ } // else
+ } // for
+
+ } finally {
+
+ if ( ReceiveBuffer != NULL ) {
+ FREE_POOL( ReceiveBuffer );
+ }
+
+ if ( ReceiveMdl != NULL ) {
+ FREE_MDL( ReceiveMdl );
+ }
+
+ if ( AllocatedIrpContext ) {
+ NwFreeExtraIrpContext( pNewIrpContext );
+ }
+
+ if (pLastNpScb) {
+ NwDereferenceScb( pLastNpScb );
+ }
+
+ }
+
+ if ( !FoundServer ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ }
+
+ return pNpScb;
+}
+
+
+NTSTATUS
+ProcessFindNearest(
+ IN struct _IRP_CONTEXT* pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the full address of the remote server and builds
+ the corresponding TA_IPX_ADDRESS.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG ResponseCount;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "ProcessFindNearest...\n", 0);
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // Timeout.
+ //
+
+ pIrpContext->ResponseParameters.Error = 0;
+ pIrpContext->pNpScb->OkToReceive = FALSE;
+
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+#if NWDBG
+ pIrpContext->DebugValue = 0x101;
+#endif
+ NwSetIrpContextEvent( pIrpContext );
+ DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ if ( BytesAvailable >= FIND_NEAREST_RESP_SIZE &&
+ Response[0] == 0 &&
+ Response[1] == SAP_SERVICE_TYPE_SERVER ) {
+
+ //
+ // This is a valid find nearest response. Process the packet.
+ //
+
+ ResponseCount = pIrpContext->Specific.Create.FindNearestResponseCount++;
+ ASSERT( ResponseCount < MAX_SAP_RESPONSES );
+
+ //
+ // Copy the Find Nearest server response to the receive buffer.
+ //
+
+ RtlCopyMemory(
+ pIrpContext->Specific.Create.FindNearestResponse[ResponseCount],
+ Response,
+ FIND_NEAREST_RESP_SIZE );
+
+ //
+ // If we have reached critical mass on the number of find
+ // nearest responses, set the event to indicate that we
+ // are done.
+ //
+
+ if ( ResponseCount == MAX_SAP_RESPONSES - 1 ) {
+
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+#ifdef NWDBG
+ pIrpContext->DebugValue = 0x102;
+#endif
+ pIrpContext->ResponseParameters.Error = 0;
+ NwSetIrpContextEvent( pIrpContext );
+
+ } else {
+ pIrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+ } else {
+
+ //
+ // Discard the invalid find nearest response.
+ //
+
+ pIrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+}
+
+BOOLEAN
+ProcessFindNearestEntry(
+ PIRP_CONTEXT IrpContext,
+ PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
+ )
+{
+ OEM_STRING OemServerName;
+ UNICODE_STRING UidServerName;
+ UNICODE_STRING ServerName;
+ NTSTATUS Status;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb = NULL;
+ BOOLEAN ExistingScb = TRUE;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "ProcessFindNearestEntry\n", 0);
+
+ ServerName.Buffer = NULL;
+ UidServerName.Buffer = NULL;
+
+ try {
+
+ RtlInitString( &OemServerName, FindNearestResponse->ServerName );
+ ASSERT( OemServerName.Length < MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) );
+
+ Status = RtlOemStringToCountedUnicodeString(
+ &ServerName,
+ &OemServerName,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ try_return( NOTHING );
+ }
+
+ //
+ // Lookup of the SCB by name. If it is not found, an SCB
+ // will be created.
+ //
+
+ Status = MakeUidServer(
+ &UidServerName,
+ &IrpContext->Specific.Create.UserUid,
+ &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( NOTHING );
+ }
+
+ ExistingScb = NwFindScb( &pScb, IrpContext, &UidServerName, &ServerName );
+ ASSERT( pScb != NULL );
+ pNpScb = pScb->pNpScb;
+
+ //
+ // Copy the network address to the SCB, and calculate the
+ // IPX address.
+ //
+
+ RtlCopyMemory(
+ &pNpScb->ServerAddress,
+ &FindNearestResponse->Network,
+ sizeof( TDI_ADDRESS_IPX ) );
+
+ BuildIpxAddress(
+ pNpScb->ServerAddress.Net,
+ pNpScb->ServerAddress.Node,
+ NCP_SOCKET,
+ &pNpScb->RemoteAddress );
+
+ if ( pNpScb->State == SCB_STATE_ATTACHING ) {
+
+ //
+ // We are in the process of trying to connect to this
+ // server so mark it reconnect required so that
+ // CreateScb will know that we've found it address.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ if ( pNpScb != NULL ) {
+ NwDereferenceScb( pNpScb );
+ }
+
+ if (UidServerName.Buffer != NULL) {
+ FREE_POOL(UidServerName.Buffer);
+ }
+
+ RtlFreeUnicodeString( &ServerName );
+ }
+
+ //
+ // Tell the caller if we created a new Scb
+ //
+
+
+ if (ExistingScb) {
+ DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", FALSE );
+ return FALSE;
+ } else {
+ DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", TRUE );
+ return TRUE;
+ }
+}
+
+
+NTSTATUS
+ConnectToServer(
+ IN struct _IRP_CONTEXT* pIrpContext,
+ OUT PSCB *pScbCollision
+ )
+/*++
+
+Routine Description:
+
+ This routine transfers connect and negotiate buffer NCPs to the server.
+
+ This routine may be called upon to connect an anonymous scb. Upon
+ learning the name of the anonymous scb, it will determine if another
+ create has completed while the name lookup was in progress. If it has,
+ then the routine will refer the called to that new scb. Otherwise, the
+ scb will be entered onto the scb list and used normally. The RCB
+ protects the scb list by name only. For more info, see the comment
+ in CreateScb().
+
+Arguments:
+
+ pIrpContext - supplies context and server information
+
+Return Value:
+
+ Status of operation
+
+--*/
+{
+ NTSTATUS Status, BurstStatus;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ PSCB pScb = pNpScb->pScb;
+ BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
+ ULONG MaxSafeSize ;
+ BOOLEAN LIPNegotiated ;
+ PLOGON Logon;
+
+ OEM_STRING OemServerName;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING CredentialName;
+ PUNICODE_STRING puConnectName;
+ BYTE OemName[MAX_SERVER_NAME_LENGTH];
+ WCHAR Server[MAX_SERVER_NAME_LENGTH];
+ KIRQL OldIrql;
+ UNICODE_STRING UidServerName;
+ BOOLEAN Success;
+ PLIST_ENTRY ScbQueueEntry;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+
+ PAGED_CODE();
+
+ DebugTrace( +0, Dbg, " Connect\n", 0);
+
+ //
+ // Get the tick count for our connection to this server
+ //
+
+ Status = GetTickCount( pIrpContext, &pNpScb->TickCount );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pNpScb->TickCount = DEFAULT_TICK_COUNT;
+ }
+
+ pNpScb->SendTimeout = pNpScb->TickCount + 10;
+
+ //
+ // Initialize timers for a server that supports burst but not LIP
+ //
+
+ pNpScb->NwLoopTime = pNpScb->NwSingleBurstPacketTime = pNpScb->SendTimeout;
+ pNpScb->NwReceiveDelay = pNpScb->NwSendDelay = 0;
+
+ pNpScb->NtSendDelay.QuadPart = 0;
+
+ //
+ // Request connection
+ //
+
+ Status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "C-");
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if (!NT_SUCCESS(Status)) {
+ if ( Status == STATUS_UNSUCCESSFUL ) {
+#ifdef QFE_BUILD
+ Status = STATUS_TOO_MANY_SESSIONS;
+#else
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+#endif
+ pNpScb->State = SCB_STATE_ATTACHING;
+
+ } else if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
+
+ //
+ // The connect timed out, suspect that the server is down
+ // and put it back in the attaching state.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ return( Status );
+ }
+
+ pNpScb->SequenceNo++;
+
+ Stats.Sessions++;
+
+ //
+ // Get server information
+ //
+
+ DebugTrace( +0, Dbg, "Get file server information\n", 0);
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_ADMIN_FUNCTION, NCP_GET_SERVER_INFO );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nrbb",
+ OemName,
+ MAX_SERVER_NAME_LENGTH,
+ &pScb->MajorVersion,
+ &pScb->MinorVersion );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // If this was an anonymous SCB, we need to check the name
+ // for a create collision before we do anything else.
+ //
+
+ if ( AnonymousScb ) {
+
+ //
+ // Grab the RCB to protect the server prefix table. We've
+ // spent the time sending the packet to look up the server
+ // name so we are a little greedy with the RCB to help
+ // minimize the chance of a collision.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Make the uid server name.
+ //
+
+ OemServerName.Buffer = OemName;
+ OemServerName.Length = 0;
+ OemServerName.MaximumLength = sizeof( OemName );
+
+ while ( ( OemServerName.Length < MAX_SERVER_NAME_LENGTH ) &&
+ ( OemName[OemServerName.Length] != '\0' ) ) {
+ OemServerName.Length++;
+ }
+
+ ServerName.Buffer = Server;
+ ServerName.MaximumLength = sizeof( Server );
+ ServerName.Length = 0;
+
+ RtlOemStringToUnicodeString( &ServerName,
+ &OemServerName,
+ FALSE );
+
+ //
+ // If this is an extended credential create, munge the server name.
+ //
+
+ RtlInitUnicodeString( &CredentialName, NULL );
+
+ if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
+
+ Status = BuildExCredentialServerName( &ServerName,
+ pIrpContext->Specific.Create.puCredentialName,
+ &CredentialName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseRcb( &NwRcb );
+ return Status;
+ }
+
+ puConnectName = &CredentialName;
+
+ } else {
+
+ puConnectName = &ServerName;
+ }
+
+ //
+ // Tack on the uid.
+ //
+
+ Status = MakeUidServer( &UidServerName,
+ &pScb->UserUid,
+ puConnectName );
+
+ if ( CredentialName.Buffer ) {
+ FREE_POOL( CredentialName.Buffer );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseRcb( &NwRcb );
+ return Status;
+ }
+
+ //
+ // Actually do the look up in the prefix table.
+ //
+
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServerName, 0 );
+
+ if ( PrefixEntry != NULL ) {
+
+ //
+ // There was a collision with this anonymous create. Dump
+ // the anonymous scb and pick up the new one.
+ //
+
+ NwReleaseRcb( &NwRcb );
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Anonymous create collided for %wZ.\n", &UidServerName );
+
+ //
+ // Disconnect this connection so we don't clutter the server.
+ //
+
+ ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "D-" );
+
+ FREE_POOL( UidServerName.Buffer );
+
+ //
+ // Since there was a collision, we know for a fact that there's another
+ // good SCB for this server somewhere. We set the state on this anonymous
+ // SCB to SCB_STATE_FLAG_SHUTDOWN so that no one ever plays with the
+ // anonymous SCB again. The scavenger will clean it up soon.
+ //
+
+ pNpScb->State = SCB_STATE_FLAG_SHUTDOWN;
+
+ if ( pScbCollision ) {
+ *pScbCollision = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+ NwReferenceScb( (*pScbCollision)->pNpScb );
+ return STATUS_SUCCESS;
+ } else {
+ DebugTrace( 0, Dbg, "Invalid path for an anonymous create.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ }
+
+ //
+ // This anonymous create didn't collide - cool! Fill in the server
+ // name, check the preferred, server setting, and put the SCB on the
+ // SCB queue in the correct location. This code is similar to pieces
+ // of code in NwAllocateAndInitScb() and NwFindScb().
+ //
+
+ DebugTrace( 0, Dbg, "Completing anonymous create for %wZ!\n", &UidServerName );
+
+ RtlCopyUnicodeString ( &pScb->UidServerName, &UidServerName );
+ pScb->UidServerName.Buffer[ UidServerName.Length / sizeof( WCHAR ) ] = L'\0';
+
+ pScb->UnicodeUid = pScb->UidServerName;
+ pScb->UnicodeUid.Length = UidServerName.Length -
+ puConnectName->Length -
+ sizeof(WCHAR);
+
+ //
+ // Make ServerName point partway down the buffer for UidServerName
+ //
+
+ pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
+ UidServerName.Length - puConnectName->Length);
+
+ pNpScb->ServerName.MaximumLength = puConnectName->Length;
+ pNpScb->ServerName.Length = puConnectName->Length;
+
+ //
+ // Determine if this is our preferred server.
+ //
+
+ Logon = FindUser( &pScb->UserUid, FALSE );
+
+ if (( Logon != NULL) &&
+ (RtlCompareUnicodeString( puConnectName, &Logon->ServerName, TRUE ) == 0 )) {
+ pScb->PreferredServer = TRUE;
+ NwReferenceScb( pNpScb );
+ }
+
+ FREE_POOL( UidServerName.Buffer );
+
+ //
+ // Insert the name of this server into the prefix table.
+ //
+
+ Success = RtlInsertUnicodePrefix( &NwRcb.ServerNameTable,
+ &pScb->UidServerName,
+ &pScb->PrefixEntry );
+
+#ifdef NWDBG
+ if ( !Success ) {
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
+ DbgBreakPoint();
+ }
+#endif
+
+ //
+ // This create is complete, release the RCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // If this is our preferred server, we have to move this guy
+ // to the head of the scb list. We do this after the create
+ // because we can't acquire the ScbSpinLock while holding the
+ // RCB.
+ //
+
+ if ( pScb->PreferredServer ) {
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+ RemoveEntryList( &pNpScb->ScbLinks );
+ InsertHeadList( &ScbQueue, &pNpScb->ScbLinks );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ }
+
+ }
+
+ if ( pScb->MajorVersion == 2 ) {
+
+ Stats.NW2xConnects++;
+ pNpScb->PageAlign = TRUE;
+
+ } else if ( pScb->MajorVersion == 3 ) {
+
+ Stats.NW3xConnects++;
+
+ if (pScb->MinorVersion > 0xb) {
+ pNpScb->PageAlign = FALSE;
+ } else {
+ pNpScb->PageAlign = TRUE;
+ }
+
+ } else if ( pScb->MajorVersion == 4 ) {
+
+ Stats.NW4xConnects++;
+ pNpScb->PageAlign = FALSE;
+
+ NdsPing( pIrpContext, pScb );
+
+ }
+
+ //
+ // Get the local net max packet size. This is the max frame size
+ // does not include space for IPX or lower level headers.
+ //
+
+ Status = GetMaximumPacketSize( pIrpContext, &pNpScb->Server, &pNpScb->MaxPacketSize );
+
+ //
+ // If the transport won't tell us, pick the largest size that
+ // is guaranteed to work.
+ //
+ if ( !NT_SUCCESS( Status ) ) {
+ pNpScb->BufferSize = DEFAULT_PACKET_SIZE;
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+ } else {
+ pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
+ }
+ MaxSafeSize = pNpScb->MaxPacketSize ;
+
+ //
+ // Negotiate a burst mode connection. Keep track of that status.
+ //
+
+ Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
+ BurstStatus = Status ;
+
+ if (!NT_SUCCESS(Status) || !LIPNegotiated) {
+
+ //
+ // Negotiate buffer size with server if we didnt do burst
+ // sucessfully or if burst succeeded but we didnt do LIP.
+ //
+
+ DebugTrace( +0, Dbg, "Negotiate Buffer Size\n", 0);
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "Fw",
+ NCP_NEGOTIATE_BUFFER_SIZE,
+ pNpScb->BufferSize );
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+ DebugTrace( +0, Dbg, " Parse response\n", 0);
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nw",
+ &pNpScb->BufferSize );
+
+ //
+ // Dont allow the server to fool us into using a
+ // packet size bigger than what the media can support.
+ // We have at least one case of server returning 4K while
+ // on ethernet.
+ //
+ // Use PacketThreshold so that the PacketAdjustment can be
+ // avoided on small packet sizes such as those on ethernet.
+ //
+
+ if (MaxSafeSize > (ULONG)PacketThreshold) {
+ MaxSafeSize -= (ULONG)LargePacketAdjustment;
+ }
+
+ //
+ // If larger than number we got from transport, taking in account
+ // IPX header (30) & NCP header (BURST_RESPONSE is a good worst
+ // case), we adjust accordingly.
+ //
+ if (pNpScb->BufferSize >
+ (MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))))
+ {
+ pNpScb->BufferSize = (USHORT)
+ (MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))) ;
+ }
+
+ //
+ // An SFT III server responded with a BufferSize of 0!
+ //
+
+ pNpScb->BufferSize = MAX(pNpScb->BufferSize,DEFAULT_PACKET_SIZE);
+
+ //
+ // If an explicit registry default was set, we honour that.
+ // Note that this only applies in the 'default' case, ie. we
+ // didnt negotiate LIP successfully. Typically, we dont
+ // expect to use this, because the server will drop to 512 if
+ // it finds routers in between. But if for some reason the server
+ // came back with a number that was higher than what some router
+ // in between can take, we have this as manual override.
+ //
+
+ if (DefaultMaxPacketSize != 0)
+ {
+ pNpScb->BufferSize = MIN (pNpScb->BufferSize,
+ (USHORT)DefaultMaxPacketSize) ;
+ }
+ }
+
+ if (NT_SUCCESS(BurstStatus)) {
+ //
+ // We negotiated burst but not LIP. Save the packet size we
+ // have from above and renegotiate the burst so that the
+ // server knows how much it can send to us. And then take
+ // the minimum of the two to make sure we are safe.
+ //
+ USHORT SavedPacketSize = pNpScb->BufferSize ;
+
+ Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
+
+ pNpScb->BufferSize = MIN(pNpScb->BufferSize,SavedPacketSize) ;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ BOOLEAN *LIPNegotiated
+ )
+/*++
+
+Routine Description:
+
+ This routine negotiates a burst mode connection with the specified
+ server.
+
+Arguments:
+
+ pIrpContext - Supplies context and server information.
+
+ pNpScb - A pointer to the NONPAGED_SCB for the server we are
+ negotiating with.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ *LIPNegotiated = FALSE ;
+
+ if (pNpScb->MaxPacketSize == DEFAULT_PACKET_SIZE) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ if ( NwBurstModeEnabled ) {
+
+ pNpScb->BurstRenegotiateReqd = TRUE;
+
+ pNpScb->SourceConnectionId = rand();
+ pNpScb->MaxSendSize = NwMaxSendSize;
+ pNpScb->MaxReceiveSize = NwMaxReceiveSize;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FDdWdd",
+ NCP_NEGOTIATE_BURST_CONNECTION,
+ pNpScb->SourceConnectionId,
+ pNpScb->BufferSize,
+ pNpScb->Burst.Socket,
+ pNpScb->MaxSendSize,
+ pNpScb->MaxReceiveSize );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Ned",
+ &pNpScb->DestinationConnectionId,
+ &pNpScb->MaxPacketSize );
+
+ if (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE) {
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+ }
+ }
+
+ if ( NT_SUCCESS( Status )) {
+
+ if (NT_SUCCESS(GetMaxPacketSize( pIrpContext, pNpScb ))) {
+ *LIPNegotiated = TRUE ;
+ }
+
+ pNpScb->SendBurstModeEnabled = TRUE;
+ pNpScb->ReceiveBurstModeEnabled = TRUE;
+
+ //
+ // Use this size as the max read and write size instead of
+ // negotiating. This is what the VLM client does and is
+ // important because the negotiate will give a smaller value.
+ //
+
+ pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+
+VOID
+RenegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine renegotiates a burst mode connection with the specified
+ server. I don't know why we need this but it seems to be required
+ by Netware latest burst implementation.
+
+Arguments:
+
+ pIrpContext - Supplies context and server information.
+
+ pNpScb - A pointer to the NONPAGED_SCB for the server we are
+ negotiating with.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Re-negotiating burst mode.\n", 0);
+
+ pNpScb->SourceConnectionId = rand();
+ pNpScb->MaxSendSize = NwMaxSendSize;
+ pNpScb->MaxReceiveSize = NwMaxReceiveSize;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FDdWdd",
+ NCP_NEGOTIATE_BURST_CONNECTION,
+ pNpScb->SourceConnectionId,
+ pNpScb->MaxPacketSize,
+ pNpScb->Burst.Socket,
+ pNpScb->MaxSendSize,
+ pNpScb->MaxReceiveSize );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Ned",
+ &pNpScb->DestinationConnectionId,
+ &pNpScb->MaxPacketSize );
+
+ //
+ // Randomly downgrade the max burst size, because that is what
+ // the netware server does, and the new burst NLM requires.
+ //
+
+ pNpScb->MaxPacketSize -= 66;
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ||
+ (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE)) {
+
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+ pNpScb->SendBurstModeEnabled = FALSE;
+ pNpScb->ReceiveBurstModeEnabled = FALSE;
+
+ } else {
+
+ //
+ // Use this size as the max read and write size instead of
+ // negotiating. This is what the VLM client does and is
+ // important because the negotiate will give a smaller value.
+ //
+
+ pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
+
+ }
+}
+
+
+NTSTATUS
+GetMaxPacketSize(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to use the LIP protocol to find the true MTU of
+ the network.
+
+Arguments:
+
+ pIrpContext - Supplies context and server information.
+
+ pNpScb - A pointer to the NONPAGED_SCB for the server we are '
+ negotiating with.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUSHORT Buffer = NULL;
+ int index, value;
+ PMDL PartialMdl = NULL, FullMdl = NULL;
+ PMDL ReceiveMdl;
+ NTSTATUS Status;
+ USHORT EchoSocket, LipPacketSize = 0;
+ int MinPacketSize, MaxPacketSize, CurrentPacketSize;
+ ULONG RxMdlLength = MdlLength(pIrpContext->RxMdl); // Save so we can restore it on exit.
+
+ BOOLEAN SecondTime = FALSE;
+ LARGE_INTEGER StartTime, Now, FirstPing, SecondPing, temp;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, DEBUG_TRACE_LIP, "GetMaxPacketSize...\n", 0);
+
+ //
+ // Negotiate LIP, attempt to negotiate a buffer of full network
+ // size.
+ //
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Fwb",
+ NCP_NEGOTIATE_LIP_CONNECTION,
+ pNpScb->BufferSize,
+ 0 ); // Flags
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nwx",
+ &LipPacketSize,
+ &EchoSocket );
+ }
+
+ //
+ // Speedup RAS
+ //
+
+ MaxPacketSize = (int) LipPacketSize - LipPacketAdjustment ;
+
+ if (( !NT_SUCCESS( Status )) ||
+ ( MaxPacketSize <= DEFAULT_PACKET_SIZE ) ||
+ ( EchoSocket == 0 )) {
+
+ //
+ // The server does not support LIP.
+ // Portable NW gives no error but socket 0.
+ // We have a report of a 3.11 server returning MaxPacketSize 0
+ //
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Account for the IPX header, which is not counted in
+ // the reported packet size. This causes problems for
+ // servers with poorly written net card drivers that
+ // abend when they get an oversize packet.
+ //
+ // This was reported by Richard Florance (richfl).
+ //
+
+ MaxPacketSize -= 30;
+
+ pNpScb->EchoCounter = MaxPacketSize;
+
+ //
+ // We will use the Echo address for the LIP protocol.
+ //
+
+ BuildIpxAddress(
+ pNpScb->ServerAddress.Net,
+ pNpScb->ServerAddress.Node,
+ EchoSocket,
+ &pNpScb->EchoAddress );
+
+ try {
+
+ Buffer = ALLOCATE_POOL_EX( NonPagedPool, MaxPacketSize );
+
+ //
+ // Avoid RAS compression algorithm from making the large and small
+ // buffers the same length since we want to see the difference in
+ // transmission times.
+ //
+
+ for (index = 0, value = 0; index < MaxPacketSize/2; index++, value++) {
+ Buffer[index] = value;
+ }
+
+ FullMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
+ if ( FullMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ PartialMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
+ if ( PartialMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ ReceiveMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
+ if ( ReceiveMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ } except( NwExceptionFilter( pIrpContext->pOriginalIrp, GetExceptionInformation() )) {
+
+ if ( Buffer != NULL ) {
+ FREE_POOL( Buffer );
+ }
+
+ if ( FullMdl != NULL ) {
+ FREE_MDL( FullMdl );
+ }
+
+ if ( PartialMdl != NULL ) {
+ FREE_MDL( FullMdl );
+ }
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ MmBuildMdlForNonPagedPool( FullMdl );
+
+ //
+ // Allocate a receive MDL and chain in to the IrpContext receive MDL.
+ //
+
+ pIrpContext->RxMdl->ByteCount = sizeof( NCP_RESPONSE ) + sizeof(ULONG);
+ MmBuildMdlForNonPagedPool( ReceiveMdl );
+ pIrpContext->RxMdl->Next = ReceiveMdl;
+
+ CurrentPacketSize = MaxPacketSize;
+ MinPacketSize = DEFAULT_PACKET_SIZE;
+
+ // Log values before we update them.
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
+
+ //
+ // Loop using the bisection method to find the maximum packet size. Feel free to
+ // use shortcuts to avoid unnecessary timeouts.
+ //
+
+ while (TRUE) {
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Sending %d byte echo\n", CurrentPacketSize );
+
+ IoBuildPartialMdl(
+ FullMdl,
+ PartialMdl,
+ Buffer,
+ CurrentPacketSize - sizeof(NCP_RESPONSE) - sizeof(ULONG) );
+
+ //
+ // Send an echo packet. If we get a response, then we know that
+ // the minimum packet size we can use is at least as big as the
+ // echo packet size.
+ //
+
+ pIrpContext->pTdiStruct = &pIrpContext->pNpScb->Echo;
+
+ //
+ // Short-circuit the even better RAS compression.
+ //
+
+ for ( index = 0; index < MaxPacketSize/2; index++, value++) {
+ Buffer[index] = value;
+ }
+
+ KeQuerySystemTime( &StartTime );
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "E_Df",
+ sizeof(NCP_RESPONSE ),
+ pNpScb->EchoCounter,
+ PartialMdl );
+
+ if (( Status != STATUS_REMOTE_NOT_LISTENING ) ||
+ ( SecondTime )) {
+
+ KeQuerySystemTime( &Now );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Response received %08lx\n", Status);
+
+ if (!SecondTime) {
+
+ MinPacketSize = CurrentPacketSize;
+ FirstPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
+ }
+
+ } else {
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "No response\n", 0);
+ MaxPacketSize = CurrentPacketSize;
+ }
+
+ pNpScb->EchoCounter++;
+ MmPrepareMdlForReuse( PartialMdl );
+
+
+ if (( MaxPacketSize - MinPacketSize <= LipAccuracy ) ||
+ ( SecondTime )) {
+
+ //
+ // We have the maximum packet size.
+ // Now - StartTime is how long it takes for the round-trip. Now we'll
+ // try the same thing with a small packet and see how long it takes. From
+ // this we'll derive a throughput rating.
+ //
+
+
+ if ( SecondTime) {
+
+ SecondPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
+ break;
+
+ } else {
+ SecondTime = TRUE;
+ // Use a small packet size to verify that the server is still up.
+ CurrentPacketSize = sizeof(NCP_RESPONSE) + sizeof(ULONG) * 2;
+ }
+
+ } else {
+
+ //
+ // Calculate the next packet size guess.
+ //
+
+ if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
+ ( MaxPacketSize == 1463 )) {
+
+ CurrentPacketSize = 1458;
+
+ } else if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
+ ( MaxPacketSize == 1458 )) {
+
+ CurrentPacketSize = 1436;
+
+ } else {
+
+ //
+ // We didn't try one of our standard sizes so use the chop search
+ // to get to the next value.
+ //
+
+ CurrentPacketSize = ( MaxPacketSize + MinPacketSize ) / 2;
+
+ }
+ }
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Set maximum burst packet size to %d\n", MinPacketSize );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing H = %08lx\n", FirstPing.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing L = %08lx\n", FirstPing.LowPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing H = %08lx\n", SecondPing.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing L = %08lx\n", SecondPing.LowPart );
+
+ //
+ // Avoid a divide by zero error if something bad happened.
+ //
+
+ if ( FirstPing.QuadPart != 0 ) {
+ pNpScb->LipDataSpeed = (ULONG) ( ( (LONGLONG)MinPacketSize * (LONGLONG)1600000 )
+ / FirstPing.QuadPart );
+ } else {
+ pNpScb->LipDataSpeed = 0;
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "LipDataSpeed: %d\n", pNpScb->LipDataSpeed );
+
+ if ((NT_SUCCESS(Status)) &&
+ ( MinPacketSize > DEFAULT_PACKET_SIZE )) {
+
+ temp.QuadPart = FirstPing.QuadPart - SecondPing.QuadPart;
+
+ if (temp.QuadPart > 0) {
+
+ //
+ // Convert to single trip instead of both ways.
+ //
+
+ temp.QuadPart = temp.QuadPart / (2 * 1000);
+
+ } else {
+
+ //
+ // Small packet ping is slower or the same speed as the big ping.
+ // We can't time a small enough interval so go for no delay at all.
+ //
+
+ temp.QuadPart = 0;
+
+ }
+
+
+ ASSERT(temp.HighPart == 0);
+
+ pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay =
+ MAX(temp.LowPart, (ULONG)MinSendDelay);
+
+ pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay =
+ MAX(temp.LowPart, (ULONG)MinReceiveDelay);
+
+ //
+ // Time for a big packet to go one way.
+ //
+
+ pNpScb->NwSingleBurstPacketTime = pNpScb->NwReceiveDelay;
+
+ pNpScb->NtSendDelay.QuadPart = pNpScb->NwReceiveDelay * -1000;
+
+
+ //
+ // Maximum that SendDelay is allowed to reach
+ //
+
+ pNpScb->NwMaxSendDelay = MAX( 52, MIN( pNpScb->NwSendDelay, MaxSendDelay ));
+ pNpScb->NwMaxReceiveDelay = MAX( 52, MIN( pNpScb->NwReceiveDelay, MaxReceiveDelay ));
+
+ //
+ // Time for a small packet to get to the server and back.
+ //
+
+ temp.QuadPart = SecondPing.QuadPart / 1000;
+ pNpScb->NwLoopTime = temp.LowPart;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxSendDelay = %08lx\n", pNpScb->NwMaxSendDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxReceiveDelay = %08lx\n", pNpScb->NwMaxReceiveDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwLoopTime = %08lx\n", pNpScb->NwLoopTime );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
+
+ //
+ // Reset Tdi struct so that we send future NCPs from the server socket.
+ //
+
+ pIrpContext->pTdiStruct = NULL;
+
+ //
+ // Now decouple the MDL
+ //
+
+ pIrpContext->TxMdl->Next = NULL;
+ pIrpContext->RxMdl->Next = NULL;
+ pIrpContext->RxMdl->ByteCount = RxMdlLength;
+
+ //
+ // Calculate the maximum amount of data we can send in a burst write
+ // packet after all the header info is stripped.
+ //
+ // BUGBUG - This is what Novell does, but real header isn't that big
+ // can we do better?
+ //
+
+ pNpScb->MaxPacketSize = MinPacketSize - sizeof( NCP_BURST_WRITE_REQUEST );
+
+ FREE_MDL( PartialMdl );
+ FREE_MDL( ReceiveMdl );
+ FREE_MDL( FullMdl );
+ FREE_POOL( Buffer );
+
+
+ DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // If the small packet couldn't echo then assume the worst.
+ //
+
+ //
+ // Reset Tdi struct so that we send future NCPs from the server socket.
+ //
+
+ pIrpContext->pTdiStruct = NULL;
+
+ //
+ // Now decouple the MDL
+ //
+
+ pIrpContext->TxMdl->Next = NULL;
+ pIrpContext->RxMdl->Next = NULL;
+ pIrpContext->RxMdl->ByteCount = RxMdlLength;
+
+ FREE_MDL( PartialMdl );
+ FREE_MDL( ReceiveMdl );
+ FREE_MDL( FullMdl );
+ FREE_POOL( Buffer );
+
+
+ DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+}
+
+
+VOID
+DestroyAllScb(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine destroys all server control blocks.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ DebugTrace(+1, Dbg, "DestroyAllScbs....\n", 0);
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ //
+ // Walk the list of SCBs and kill them all.
+ //
+
+ while (!IsListEmpty(&ScbQueue)) {
+
+ ScbQueueEntry = RemoveHeadList( &ScbQueue );
+ pNpScb = CONTAINING_RECORD(ScbQueueEntry, NONPAGED_SCB, ScbLinks);
+
+ //
+ // We can't hold the spin lock while deleting an SCB, so release
+ // it now.
+ //
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ NwDeleteScb( pNpScb->pScb );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ DebugTrace(-1, Dbg, "DestroyAllScb\n", 0 );
+}
+
+
+VOID
+NwDeleteScb(
+ PSCB pScb
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes an SCB. The SCB must not be in use.
+
+ *** The caller must own the RCB exclusive.
+
+Arguments:
+
+ Scb - The SCB to delete
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDeleteScb...\n", 0);
+
+ pNpScb = pScb->pNpScb;
+
+ //
+ // Make sure we are not deleting a logged in connection
+ // or we will hang up the license until the server times
+ // it out.
+ //
+
+ ASSERT( pNpScb->State != SCB_STATE_IN_USE );
+ ASSERT( pNpScb->Reference == 0 );
+ ASSERT( !pNpScb->Sending );
+ ASSERT( !pNpScb->Receiving );
+ ASSERT( !pNpScb->OkToReceive );
+ ASSERT( IsListEmpty( &pNpScb->Requests ) );
+ ASSERT( IsListEmpty( &pScb->IcbList ) );
+ ASSERT( pScb->IcbCount == 0 );
+ ASSERT( pScb->VcbCount == 0 );
+
+
+ DebugTrace(0, Dbg, "Cleaning up SCB %08lx\n", pScb);
+
+ if ( AnonymousScb ) {
+ DebugTrace(0, Dbg, "SCB is anonymous\n", &pNpScb->ServerName );
+ } else {
+ ASSERT( IsListEmpty( &pScb->ScbSpecificVcbQueue ) );
+ DebugTrace(0, Dbg, "SCB is %wZ\n", &pNpScb->ServerName );
+ }
+
+ DebugTrace(0, Dbg, "SCB state is %d\n", &pNpScb->State );
+
+ if ( !AnonymousScb ) {
+ RtlRemoveUnicodePrefix ( &NwRcb.ServerNameTable, &pScb->PrefixEntry );
+ }
+
+ IPX_Close_Socket( &pNpScb->Server );
+ IPX_Close_Socket( &pNpScb->WatchDog );
+ IPX_Close_Socket( &pNpScb->Send );
+ IPX_Close_Socket( &pNpScb->Echo);
+ IPX_Close_Socket( &pNpScb->Burst);
+
+ FREE_POOL( pNpScb );
+
+ if ( pScb->UserName.Buffer != NULL ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ }
+
+ FREE_POOL( pScb );
+
+ DebugTrace(-1, Dbg, "NwDeleteScb -> VOID\n", 0);
+}
+
+
+PNONPAGED_SCB
+SelectConnection(
+ PNONPAGED_SCB NpScb OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Find a default server (which is also the nearest server).
+ If NpScb is not supplied, simply return the first server in
+ the list. If it is supplied return the next server in the
+ list after the given server.
+
+Arguments:
+
+ NpScb - The starting point for the server search.
+
+Return Value:
+
+ Scb to be used or NULL.
+
+--*/
+{
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNextNpScb;
+
+ DebugTrace(+1, Dbg, "SelectConnection....\n", 0);
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ if ( NpScb == NULL ) {
+ ScbQueueEntry = ScbQueue.Flink ;
+ } else {
+ ScbQueueEntry = NpScb->ScbLinks.Flink;
+ }
+
+ for ( ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = ScbQueueEntry->Flink ) {
+
+ pNextNpScb = CONTAINING_RECORD(
+ ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+
+ //
+ // Check to make sure that this SCB is usable.
+ //
+
+ if (( pNextNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
+ ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
+ ( pNextNpScb->State == SCB_STATE_IN_USE )) {
+
+ NwReferenceScb( pNextNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+ DebugTrace(+0, Dbg, " NpScb = %lx\n", pNextNpScb );
+ DebugTrace(-1, Dbg, " NpScb->State = %x\n", pNextNpScb->State );
+ return pNextNpScb;
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+ DebugTrace(-1, Dbg, " NULL\n", 0);
+ return NULL;
+}
+
+
+VOID
+NwLogoffAllServers(
+ PIRP_CONTEXT pIrpContext,
+ PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a logoff to all connected servers created by the Logon
+ user or all servers if Logon is NULL.
+
+Arguments:
+
+ Uid - Supplies the servers to disconnect from.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY NextScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ DebugTrace( 0, Dbg, "NwLogoffAllServers\n", 0 );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ //
+ // Reference the SCB so that it doesn't disappear while we
+ // are disconnecting.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the SCB spin lock so that we can send a logoff
+ // NCP.
+ //
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Destroy this Scb if its not the permanent Scb and either we
+ // are destroying all Scb's or it was created for this user.
+ //
+
+ if (( pNpScb->pScb != NULL ) &&
+ (( Uid == NULL ) ||
+ ( pNpScb->pScb->UserUid.QuadPart == (*Uid).QuadPart))) {
+
+ NwLogoffAndDisconnect( pIrpContext, pNpScb );
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ //
+ // Release the temporary reference.
+ //
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+}
+
+
+VOID
+NwLogoffAndDisconnect(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a logoff and disconnects from the name server.
+
+Arguments:
+
+ pIrpContext - A pointer to the current IRP context.
+ pNpScb - A pointer to the server to logoff and disconnect.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PSCB pScb = pNpScb->pScb;
+
+ PAGED_CODE();
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ //
+ // If we are logging out from the preferred server, free the preferred
+ // server reference.
+ //
+
+ if ( pScb != NULL &&
+ pScb->PreferredServer ) {
+ pScb->PreferredServer = FALSE;
+ NwDereferenceScb( pNpScb );
+ }
+
+ //
+ // Nothing to do if we are not connected.
+ //
+
+ if ( pNpScb->State != SCB_STATE_IN_USE &&
+ pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return;
+ }
+
+ //
+ // If we timeout then we don't want to go to the bother of
+ // reconnecting.
+ //
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Logout and disconnect.
+ //
+
+ if ( pNpScb->State == SCB_STATE_IN_USE ) {
+
+ ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F",
+ NCP_LOGOUT );
+ }
+
+ ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "D-" ); // Disconnect
+
+ Stats.Sessions--;
+
+ if ( pScb->MajorVersion == 2 ) {
+ Stats.NW2xConnects--;
+ } else if ( pScb->MajorVersion == 3 ) {
+ Stats.NW3xConnects--;
+ } else if ( pScb->MajorVersion == 4 ) {
+ Stats.NW4xConnects--;
+ }
+
+ //
+ // Free the remembered username and password.
+ //
+
+ if ( pScb != NULL && pScb->UserName.Buffer != NULL ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ RtlInitUnicodeString( &pScb->UserName, NULL );
+ RtlInitUnicodeString( &pScb->Password, NULL );
+ }
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return;
+}
+
+
+VOID
+InitializeAttach (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize global structures for attaching to servers.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ PAGED_CODE();
+
+ KeInitializeSpinLock( &ScbSpinLock );
+ InitializeListHead(&ScbQueue);
+}
+
+
+NTSTATUS
+OpenScbSockets(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Open the communications sockets for an SCB.
+
+Arguments:
+
+ pIrpContext - The IRP context pointers for the request in progress.
+
+ pNpScb - The SCB to connect to the network.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // Auto allocate to the server socket.
+ //
+
+ pNpScb->Server.Socket = 0;
+
+ Status = IPX_Open_Socket (pIrpContext, &pNpScb->Server);
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Watchdog Socket is Server.Socket+1
+ //
+
+ pNpScb->WatchDog.Socket = NextSocket( pNpScb->Server.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->WatchDog );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Send Socket is WatchDog.Socket+1
+ //
+
+ pNpScb->Send.Socket = NextSocket( pNpScb->WatchDog.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Send );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Echo socket
+ //
+
+ pNpScb->Echo.Socket = NextSocket( pNpScb->Send.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Echo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Burst socket
+ //
+
+ pNpScb->Burst.Socket = NextSocket( pNpScb->Echo.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Burst );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+DoBinderyLogon(
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+ )
+/*++
+
+Routine Description:
+
+ Performs a bindery based encrypted logon.
+
+ Note: Rcb is held exclusively so that we can access the Logon queue
+ safely.
+
+Arguments:
+
+ pIrpContext - The IRP context pointers for the request in progress.
+
+ UserName - The user name to use to login.
+
+ Password - The password to use to login.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ UNICODE_STRING Name;
+ UNICODE_STRING PWord;
+ UCHAR EncryptKey[ENCRYPTION_KEY_SIZE ];
+ NTSTATUS Status;
+ PVOID Buffer;
+ PLOGON Logon = NULL;
+ PWCH OldBuffer;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "DoBinderyLogon...\n", 0);
+
+ //
+ // First get an encryption key.
+ //
+
+ DebugTrace( +0, Dbg, " Get Login key\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_ADMIN_FUNCTION, NCP_GET_LOGIN_KEY );
+
+ pNpScb = IrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr",
+ EncryptKey, sizeof(EncryptKey) );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ //
+ // Choose a name and password to use to connect to the server. Use
+ // the user supplied if the exist. Otherwise if the server already
+ // has a remembered username use the remembered name. If nothing
+ // else is available use the defaults from logon. Finally, if the
+ // user didn't even logon, use GUEST no password.
+ //
+
+
+ if ( UserName != NULL && UserName->Buffer != NULL ) {
+
+ Name = *UserName;
+
+ } else if ( pScb->UserName.Buffer != NULL ) {
+
+ Name = pScb->UserName;
+
+ } else {
+
+ Logon = FindUser( &pScb->UserUid, FALSE );
+
+ if (Logon != NULL ) {
+ Name = Logon->UserName;
+ } else {
+ ASSERT( FALSE && "No logon record found" );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+ if ( Password != NULL && Password->Buffer != NULL ) {
+
+ PWord = *Password;
+
+ } else if ( pScb->Password.Buffer != NULL ) {
+
+ PWord = pScb->Password;
+
+ } else {
+
+ if ( Logon == NULL ) {
+ Logon = FindUser( &pScb->UserUid, FALSE );
+ }
+
+ if ( Logon != NULL ) {
+ PWord = Logon->PassWord;
+ } else {
+ ASSERT( FALSE && "No logon record found" );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Failed to get an encryption key. Login to server, plain text
+ //
+
+ DebugTrace( +0, Dbg, " Plain Text Login\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SwUU",
+ NCP_ADMIN_FUNCTION, NCP_PLAIN_TEXT_LOGIN,
+ OT_USER,
+ &Name,
+ &PWord);
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( !NT_SUCCESS( Status )) {
+ return( STATUS_WRONG_PASSWORD);
+ }
+
+ } else if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // We have an encryption key. Get the ObjectId
+ //
+
+ UCHAR Response[ENCRYPTION_KEY_SIZE];
+ UCHAR ObjectId[OBJECT_ID_SIZE];
+ OEM_STRING UOPassword;
+
+ DebugTrace( +0, Dbg, " Query users objectid\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SwU",
+ NCP_ADMIN_FUNCTION, NCP_QUERY_OBJECT_ID,
+ OT_USER,
+ &Name);
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Save the new address in a local copy so that we can logout.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr",
+ ObjectId, OBJECT_ID_SIZE );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if (!NT_SUCCESS(Status)) {
+ return( STATUS_NO_SUCH_USER );
+ }
+
+ //
+ // Convert the unicode password to uppercase and then the oem
+ // character set.
+ //
+
+ if ( PWord.Length > 0 ) {
+
+ Status = RtlUpcaseUnicodeStringToOemString( &UOPassword, &PWord, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ return( Status );
+ }
+
+ } else {
+ UOPassword.Buffer = "";
+ UOPassword.Length = UOPassword.MaximumLength = 0;
+ }
+
+ RespondToChallenge( ObjectId, &UOPassword, EncryptKey, Response);
+
+ if ( PWord.Length > 0) {
+ RtlFreeAnsiString( &UOPassword );
+ }
+
+ DebugTrace( +0, Dbg, " Encrypted Login\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SrwU",
+ NCP_ADMIN_FUNCTION, NCP_ENCRYPTED_LOGIN,
+ Response, sizeof(Response),
+ OT_USER,
+ &Name);
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Save the new address in a local copy so that we can logout
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Special case error mappings.
+ //
+
+ if (( Status == STATUS_UNSUCCESSFUL ) ||
+ ( Status == STATUS_UNEXPECTED_NETWORK_ERROR /* 2.2 servers */ )) {
+ Status = STATUS_WRONG_PASSWORD;
+ }
+
+ if ( Status == STATUS_LOCK_NOT_GRANTED ) {
+ Status = STATUS_ACCOUNT_RESTRICTION; // Bindery locked
+ }
+
+ if ( Status == STATUS_DISK_FULL ) {
+#ifdef QFE_BUILD
+ Status = STATUS_TOO_MANY_SESSIONS;
+#else
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+#endif
+ }
+
+ if ( Status == STATUS_FILE_LOCK_CONFLICT ) {
+ Status = STATUS_SHARING_PAUSED;
+ }
+
+ if ( Status == STATUS_NO_MORE_ENTRIES ) {
+ Status = STATUS_NO_SUCH_USER; // No such object on "Login Object Encrypted" NCP.
+ }
+
+ //
+ // Stupid Netware 4.x servers return a different NCP error for
+ // a disabled account (from intruder lockout) on bindery login,
+ // and nwconvert maps this to a dos error. In this special case,
+ // we'll catch it and map it back.
+ //
+
+ if ( ( IrpContext->pNpScb->pScb->MajorVersion >= 4 ) &&
+ ( Status == 0xC001003B ) ) {
+ Status = STATUS_ACCOUNT_DISABLED;
+ }
+
+ return( Status );
+ }
+
+ } else {
+
+ return( Status );
+
+ }
+
+ //
+ // If the Uid is for the system process then the username must be
+ // in the NtGateway group on the server.
+ //
+
+ if ( IrpContext->Specific.Create.UserUid.QuadPart == DefaultLuid.QuadPart) {
+
+ NTSTATUS Status1 ;
+
+ // IsBinderyObjectInSet?
+ Status1 = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SwppwU",
+ NCP_ADMIN_FUNCTION, NCP_IS_OBJECT_IN_SET,
+ OT_GROUP,
+ "NTGATEWAY",
+ "GROUP_MEMBERS",
+ OT_USER,
+ &Name);
+
+ if ( !NT_SUCCESS( Status1 ) ) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ }
+
+ //
+ // Success. Save the username & password for reconnect.
+ //
+
+ //
+ // Setup to free the old user name and password buffer.
+ //
+
+ if ( pScb->UserName.Buffer != NULL ) {
+ OldBuffer = pScb->UserName.Buffer;
+ } else {
+ OldBuffer = NULL;
+ }
+
+ Buffer = ALLOCATE_POOL( NonPagedPool, Name.Length + PWord.Length );
+ if ( Buffer == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ pScb->UserName.Buffer = Buffer;
+ pScb->UserName.Length = pScb->UserName.MaximumLength = Name.Length;
+ RtlMoveMemory( pScb->UserName.Buffer, Name.Buffer, Name.Length );
+
+ pScb->Password.Buffer = (PWCHAR)((PCHAR)Buffer + Name.Length);
+ pScb->Password.Length = pScb->Password.MaximumLength = PWord.Length;
+ RtlMoveMemory( pScb->Password.Buffer, PWord.Buffer, PWord.Length );
+
+ if ( OldBuffer != NULL ) {
+ FREE_POOL( OldBuffer );
+ }
+ return( Status );
+}
+
+NTSTATUS
+NwAllocateAndInitScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UidServerName OPTIONAL,
+ IN PUNICODE_STRING ServerName OPTIONAL,
+ OUT PSCB *ppScb
+)
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to a newly created SCB. If
+ the UidServerName and ServerName are supplied, the SCB name
+ fields are initialized to this name. Otherwise, the name
+ fields are left blank to be filled in later.
+
+ If UidServerName is provided, ServerName MUST also be provided!!
+
+ The returned SCB is NOT filed in the server prefix table since
+ it might not yet have a name.
+
+Return Value:
+
+ The created SCB or NULL.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+ USHORT ServerNameLength;
+ PLOGON Logon;
+
+ //
+ // Allocate enough space for a credential munged tree name.
+ //
+
+ pScb = ALLOCATE_POOL ( PagedPool,
+ sizeof( SCB ) +
+ ( ( NDS_TREE_NAME_LEN + MAX_NDS_NAME_CHARS + 2 ) * sizeof( WCHAR ) ) );
+
+ if ( !pScb ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory( pScb, sizeof( SCB ) );
+ RtlInitializeBitMap( &pScb->DriveMapHeader, pScb->DriveMap, MAX_DRIVES );
+
+ //
+ // Initialize pointers to ensure cleanup on error case operates
+ // correctly.
+ //
+
+ if ( UidServerName &&
+ UidServerName->Length ) {
+
+ ServerNameLength = UidServerName->Length + sizeof( WCHAR );
+
+ } else {
+
+ ServerNameLength = ( MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) ) +
+ ( MAX_UNICODE_UID_LENGTH * sizeof( WCHAR ) ) +
+ ( 2 * sizeof( WCHAR ) );
+
+ }
+
+ pScb->pNpScb = ALLOCATE_POOL ( NonPagedPool,
+ sizeof( NONPAGED_SCB ) + ServerNameLength );
+
+ if ( !pScb->pNpScb ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ RtlZeroMemory( pScb->pNpScb, sizeof( NONPAGED_SCB ) );
+
+ pNpScb = pScb->pNpScb;
+ pNpScb->pScb = pScb;
+
+ //
+ // If we know the server name, copy it to the allocated buffer.
+ // Append a NULL so that we can use the name a nul-terminated string.
+ //
+
+ pScb->UidServerName.Buffer = (PWCHAR)( pScb->pNpScb + 1 );
+ pScb->UidServerName.MaximumLength = ServerNameLength;
+ pScb->UidServerName.Length = 0;
+
+ RtlInitUnicodeString( &(pNpScb->ServerName), NULL );
+
+ if ( UidServerName &&
+ UidServerName->Length ) {
+
+ RtlCopyUnicodeString ( &pScb->UidServerName, UidServerName );
+ pScb->UidServerName.Buffer[ UidServerName->Length / sizeof( WCHAR ) ] = L'\0';
+
+ pScb->UnicodeUid = pScb->UidServerName;
+ pScb->UnicodeUid.Length = UidServerName->Length -
+ ServerName->Length -
+ sizeof(WCHAR);
+
+ //
+ // Make ServerName point partway down the buffer for UidServerName
+ //
+
+ pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
+ UidServerName->Length - ServerName->Length);
+
+ pNpScb->ServerName.MaximumLength = ServerName->Length;
+ pNpScb->ServerName.Length = ServerName->Length;
+
+ }
+
+ pScb->NdsTreeName.MaximumLength = NDS_TREE_NAME_LEN * sizeof( WCHAR );
+ pScb->NdsTreeName.Buffer = (PWCHAR)(pScb + 1);
+
+ pScb->NodeTypeCode = NW_NTC_SCB;
+ pScb->NodeByteSize = sizeof(SCB);
+ InitializeListHead( &pScb->ScbSpecificVcbQueue );
+ InitializeListHead( &pScb->IcbList );
+
+ //
+ // Remember UID of the file creator so we can find the username and
+ // password to use for this Scb when we need it.
+ //
+
+ pScb->UserUid = pIrpContext->Specific.Create.UserUid;
+
+ //
+ // Initialize the non-paged part of the SCB.
+ //
+
+ pNpScb->NodeTypeCode = NW_NTC_SCBNP;
+ pNpScb->NodeByteSize = sizeof(NONPAGED_SCB);
+
+ //
+ // Set the initial SCB reference count.
+ //
+
+ if ( UidServerName &&
+ UidServerName->Length ) {
+
+ Logon = FindUser( &pScb->UserUid, FALSE );
+
+ if (( Logon != NULL) &&
+ (RtlCompareUnicodeString( ServerName, &Logon->ServerName, TRUE ) == 0 )) {
+ pScb->PreferredServer = TRUE;
+ }
+ }
+
+ if ( pScb->PreferredServer ) {
+ pNpScb->Reference = 2;
+ } else {
+ pNpScb->Reference = 1;
+ }
+
+ //
+ // Finish linking the two parts of the Scb together.
+ //
+
+ pNpScb->pScb = pScb;
+
+ KeInitializeSpinLock( &pNpScb->NpScbSpinLock );
+ KeInitializeSpinLock( &pNpScb->NpScbInterLock );
+ InitializeListHead( &pNpScb->Requests );
+
+ RtlFillMemory( &pNpScb->LocalAddress, sizeof(IPXaddress), 0xff);
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ pNpScb->SequenceNo = 1;
+
+ Status = OpenScbSockets( pIrpContext, pNpScb );
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->WatchDog,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &WatchDogDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Send,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &SendDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Echo,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ pNpScb );
+
+ pNpScb->EchoCounter = 2;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Burst,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ KeQuerySystemTime( &pNpScb->LastUsedTime );
+
+ //
+ // Set burst mode data.
+ //
+
+ pNpScb->BurstRequestNo = 0;
+ pNpScb->BurstSequenceNo = 0;
+
+ if ( ppScb ) {
+ *ppScb = pScb;
+ }
+
+ return STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( pNpScb != NULL ) {
+
+ IPX_Close_Socket( &pNpScb->Server );
+ IPX_Close_Socket( &pNpScb->WatchDog );
+ IPX_Close_Socket( &pNpScb->Send );
+ IPX_Close_Socket( &pNpScb->Echo );
+ IPX_Close_Socket( &pNpScb->Burst );
+
+ FREE_POOL( pNpScb );
+ }
+
+ FREE_POOL(pScb);
+ return Status;
+
+}
+
+
+BOOLEAN
+NwFindScb(
+ OUT PSCB *Scb,
+ PIRP_CONTEXT IrpContext,
+ PUNICODE_STRING UidServerName,
+ PUNICODE_STRING ServerName
+ )
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to the SCB for the named server.
+ The name is looked up in the SCB table. If it is found, a
+ pointer to the SCB is returned. If none is found an SCB is
+ created and initialized.
+
+ This routine returns with the SCB referenced and the SCB
+ resources held.
+
+Arguments:
+
+ Scb - Returns a pointer to the found / created SCB.
+
+ IrpContext - The IRP context pointers for the request in progress.
+
+ ServerName - The name of the server to find / create.
+
+Return Value:
+
+ TRUE - An old SCB was found.
+
+ FALSE - A new SCB was created, or an attempt to create the SCB failed.
+
+--*/
+{
+ BOOLEAN RcbHeld;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ NTSTATUS Status;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+ KIRQL OldIrql;
+ BOOLEAN Success;
+
+ //
+ // Acquire the RCB exclusive to protect the prefix table.
+ // Then lookup the name of this server.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ RcbHeld = TRUE;
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, UidServerName, 0 );
+
+ if ( PrefixEntry != NULL ) {
+
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+
+ //
+ // We found the SCB, increment the reference count and return
+ // success.
+ //
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+ pNpScb = pScb->pNpScb;
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the RCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ DebugTrace(-1, Dbg, "NwFindScb -> %08lx\n", pScb );
+ *Scb = pScb;
+ return( TRUE );
+ }
+
+ //
+ // We do not have a connection to this server so create the new Scb if requested.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
+ NwReleaseRcb( &NwRcb );
+ *Scb = NULL;
+ return(FALSE);
+ }
+
+ try {
+
+ Status = NwAllocateAndInitScb( IrpContext,
+ UidServerName,
+ ServerName,
+ &pScb );
+
+ if ( !NT_SUCCESS( Status )) {
+ ExRaiseStatus( Status );
+ }
+
+ ASSERT( pScb != NULL );
+
+ pNpScb = pScb->pNpScb;
+
+ //
+ //*******************************************************************
+ //
+ // From this point on we must not fail to create the Scb because the
+ // another thread will be able to reference the Scb causing severe
+ // problems in the finaly clause or in the other thread.
+ //
+ //*******************************************************************
+ //
+
+ //
+ // Insert this SCB in the global list if SCBs.
+ // If it is the default (i.e. preferred) server, stick it at the
+ // front of the queue so that SelectConnection() will select it
+ // for bindery queries.
+ //
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ if ( pScb->PreferredServer ) {
+ InsertHeadList(&ScbQueue, &pNpScb->ScbLinks);
+ } else {
+ InsertTailList(&ScbQueue, &pNpScb->ScbLinks);
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Insert the name of this server into the prefix table.
+ //
+
+ Success = RtlInsertUnicodePrefix(
+ &NwRcb.ServerNameTable,
+ &pScb->UidServerName,
+ &pScb->PrefixEntry );
+
+#ifdef NWDBG
+ if ( !Success ) {
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
+ DbgBreakPoint();
+ }
+#endif
+
+ //
+ // The Scb is now in the prefix table. Any new requests for this
+ // connection can be added to the Scb->Requests queue while we
+ // attach to the server.
+ //
+
+ NwReleaseRcb( &NwRcb );
+ RcbHeld = FALSE;
+
+ //
+ // If we got an error we should have raised an exception.
+ //
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ } finally {
+
+ if ( !NT_SUCCESS( Status ) || AbnormalTermination() ) {
+ *Scb = NULL;
+ } else {
+ *Scb = pScb;
+ }
+
+ if (RcbHeld) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ }
+
+ return( FALSE );
+}
+
+NTSTATUS
+QueryServersAddress(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNearestScb,
+ PUNICODE_STRING pServerName,
+ IPXaddress *pServerAddress
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING NewServer;
+ USHORT CurrChar = 0;
+
+ PAGED_CODE();
+
+ //
+ // Unmunge the server name in case this is a
+ // supplemental credential connect.
+ //
+
+ UnmungeCredentialName( pServerName, &NewServer );
+
+ //
+ // Strip server name trailer, if it exists. If there
+ // was no trailer, the length will end up being exactly
+ // the same as when we started.
+ //
+
+ if (EnableMultipleConnects) {
+
+ while ( (CurrChar < (NewServer.Length / sizeof(WCHAR))) &&
+ NewServer.Buffer[CurrChar] != ((WCHAR)L'#') ) {
+ CurrChar++;
+ }
+ NewServer.Length = CurrChar * sizeof(WCHAR);
+
+ }
+
+ //
+ // Query the bindery of the nearest server looking for
+ // the network address of the target server.
+ //
+
+ DebugTrace( +0, Dbg, "Query servers address\n", 0);
+
+ Status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SwUbp",
+ NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
+ OT_FILESERVER,
+ &NewServer,
+ 1, // Segment number
+ NET_ADDRESS_PROPERTY );
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Save the new address.
+ //
+
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nr",
+ pServerAddress, sizeof(TDI_ADDRESS_IPX) );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ //
+ // Map the server not found error to something sensible.
+ //
+
+ if (( Status == STATUS_NO_MORE_ENTRIES ) ||
+ ( Status == STATUS_VIRTUAL_CIRCUIT_CLOSED ) ||
+ ( Status == NwErrorToNtStatus(ERROR_UNEXP_NET_ERR))) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ return( Status );
+}
+
+
+
+VOID
+TreeConnectScb(
+ IN PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ This routine increments the tree connect count for a SCB.
+
+Arguments:
+
+ Scb - A pointer to the SCB to connect to.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ Scb->AttachCount++;
+ Scb->OpenFileCount++;
+ NwReferenceScb( Scb->pNpScb );
+ NwReleaseRcb( &NwRcb );
+}
+
+
+NTSTATUS
+TreeDisconnectScb(
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ This routine decrements the tree connect count for a SCB.
+
+ *** This routine must be called with the RCB resource held.
+
+Arguments:
+
+ Scb - A pointer to the SCB to disconnect.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ if ( Scb->AttachCount > 0 ) {
+
+ Scb->AttachCount--;
+ Scb->OpenFileCount--;
+ NwDereferenceScb( Scb->pNpScb );
+
+ Status = STATUS_SUCCESS;
+
+ if ( Scb->OpenFileCount == 0 ) {
+
+ //
+ // Logoff and disconnect from the server now.
+ // Hold on to the SCB lock.
+ // This prevents another thread from trying to access
+ // SCB will this thread is logging off.
+ //
+
+ NwLogoffAndDisconnect( IrpContext, Scb->pNpScb );
+ }
+ } else {
+ // FIXFIX just return success since we are disconnected?
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ return( Status );
+}
+
+
+VOID
+ReconnectScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb
+ )
+/*++
+
+Routine Description:
+
+ This routine reconnects all the dir handles to a server
+ when reconnecting an SCB.
+
+
+Arguments:
+
+ pScb - A pointer to the SCB that has just been reconnected.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // If this is a reconnect, kill all old ICB and VCB handles
+ //
+
+ if ( pScb->VcbCount != 0 ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Invalid all ICBs
+ //
+
+ NwInvalidateAllHandlesForScb( pScb );
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // Acquire new VCB handles for all VCBs.
+ //
+
+ NwReopenVcbHandlesForScb( pIrpContext, pScb );
+ }
+}
diff --git a/private/nw/rdr/cache.c b/private/nw/rdr/cache.c
new file mode 100644
index 000000000..0729b9321
--- /dev/null
+++ b/private/nw/rdr/cache.c
@@ -0,0 +1,698 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Cache.c
+
+Abstract:
+
+ This module implements internal caching support routines. It does
+ not interact with the cache manager.
+
+Author:
+
+ Manny Weiser [MannyW] 05-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+BOOLEAN
+SpaceForWriteBehind(
+ PNONPAGED_FCB NpFcb,
+ ULONG FileOffset,
+ ULONG BytesToWrite
+ );
+
+BOOLEAN
+OkToReadAhead(
+ PFCB Fcb,
+ IN ULONG FileOffset,
+ IN UCHAR IoType
+ );
+
+#define Dbg (DEBUG_TRACE_CACHE)
+
+//
+// Local procedure prototypes
+//
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, CacheRead )
+#pragma alloc_text( PAGE, SpaceForWriteBehind )
+#pragma alloc_text( PAGE, CacheWrite )
+#pragma alloc_text( PAGE, OkToReadAhead )
+#pragma alloc_text( PAGE, CalculateReadAheadSize )
+#pragma alloc_text( PAGE, FlushCache )
+#pragma alloc_text( PAGE, AcquireFcbAndFlushCache )
+#endif
+
+
+ULONG
+CacheRead(
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToRead,
+ IN PVOID UserBuffer
+ , IN BOOLEAN WholeBufferOnly
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to satisfy a user read from cache. It returns
+ the number of bytes actually copied from cache.
+
+Arguments:
+
+ NpFcb - A pointer the the nonpaged FCB of the file being read.
+
+ FileOffset - The file offset to read.
+
+ BytesToRead - The number of bytes to read.
+
+ UserBuffer - A pointer to the users target buffer.
+
+ WholeBufferOnly - Do a cache read only if we can satisfy the entire
+ read request.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ ULONG BytesToCopy;
+
+ PAGED_CODE();
+
+ if (DisableReadCache) return 0 ;
+
+ DebugTrace(0, Dbg, "CacheRead...\n", 0 );
+ DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
+ DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToRead );
+
+ NwAcquireSharedFcb( NpFcb, TRUE );
+
+ //
+ // If this is a read ahead and it contains some data that the user
+ // could be interested in, copy the interesting data.
+ //
+
+ if ( NpFcb->CacheType == ReadAhead &&
+ NpFcb->CacheDataSize != 0 &&
+ FileOffset >= NpFcb->CacheFileOffset &&
+ FileOffset <= NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) {
+
+ if ( NpFcb->CacheBuffer ) {
+
+ //
+ // Make sure we have a CacheBuffer.
+ //
+
+ BytesToCopy =
+ MIN ( BytesToRead,
+ NpFcb->CacheFileOffset +
+ NpFcb->CacheDataSize - FileOffset );
+
+ if ( WholeBufferOnly && BytesToCopy != BytesToRead ) {
+ NwReleaseFcb( NpFcb );
+ return( 0 );
+ }
+
+ RtlCopyMemory(
+ UserBuffer,
+ NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
+ BytesToCopy );
+
+ DebugTrace(0, Dbg, "CacheRead -> %d\n", BytesToCopy );
+
+ } else {
+
+ ASSERT(FALSE); // we should never get here
+ DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
+ BytesToCopy = 0;
+ }
+
+
+ } else {
+
+ DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
+ BytesToCopy = 0;
+ }
+
+ NwReleaseFcb( NpFcb );
+ return( BytesToCopy );
+}
+
+
+BOOLEAN
+SpaceForWriteBehind(
+ PNONPAGED_FCB NpFcb,
+ ULONG FileOffset,
+ ULONG BytesToWrite
+ )
+/*++
+
+Routine Description:
+
+ This routine determines if it is ok to write behind this data to
+ this FCB.
+
+Arguments:
+
+ NpFcb - A pointer the the NONPAGED_FCB of the file being written.
+
+ FileOffset - The file offset to write.
+
+ BytesToWrite - The number of bytes to write.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ PAGED_CODE();
+
+
+ if ( NpFcb->CacheDataSize == 0 ) {
+ NpFcb->CacheFileOffset = FileOffset;
+ }
+
+ if ( NpFcb->CacheDataSize == 0 && BytesToWrite >= NpFcb->CacheSize ) {
+ return( FALSE );
+ }
+
+ if ( FileOffset - NpFcb->CacheFileOffset + BytesToWrite >
+ NpFcb->CacheSize ) {
+
+ return( FALSE );
+
+ }
+
+ return( TRUE );
+}
+
+
+BOOLEAN
+CacheWrite(
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToWrite,
+ IN PVOID UserBuffer
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to satisfy a user write to cache. The write
+ succeeds if it is sequential and fits in the cache buffer.
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the NONPAGED_FCB of the file being read.
+
+ FileOffset - The file offset to write.
+
+ BytesToWrite - The number of bytes to write.
+
+ UserBuffer - A pointer to the users source buffer.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ ULONG CacheSize;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (DisableWriteCache) return FALSE ;
+
+ DebugTrace( +1, Dbg, "CacheWrite...\n", 0 );
+ DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
+ DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToWrite );
+
+ if ( NpFcb->Fcb->ShareAccess.SharedWrite ||
+ NpFcb->Fcb->ShareAccess.SharedRead ) {
+
+ DebugTrace( 0, Dbg, "File is not open in exclusive mode\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ return( FALSE );
+ }
+
+ //
+ // Note, If we decide to send data to the server we must be at the front
+ // of the queue before we grab the Fcb exclusive.
+ //
+
+TryAgain:
+
+ NwAcquireExclusiveFcb( NpFcb, TRUE );
+
+ //
+ // Allocate a cache buffer if we don't already have one.
+ //
+
+ if ( NpFcb->CacheBuffer == NULL ) {
+
+ if ( IrpContext == NULL ) {
+ DebugTrace( 0, Dbg, "No cache buffer\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ NpFcb->CacheType = WriteBehind;
+
+ if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
+ ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
+
+ CacheSize = IrpContext->pNpScb->MaxReceiveSize;
+
+ } else {
+
+ CacheSize = IrpContext->pNpScb->BufferSize;
+
+ }
+
+ try {
+
+ NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, CacheSize );
+ NpFcb->CacheSize = CacheSize;
+
+ NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, CacheSize, FALSE, FALSE, NULL );
+
+ if ( NpFcb->CacheMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( NpFcb->CacheBuffer != NULL) {
+ FREE_POOL( NpFcb->CacheBuffer );
+
+ NpFcb->CacheBuffer = NULL;
+ NpFcb->CacheSize = 0;
+
+ }
+
+ DebugTrace( 0, Dbg, "Allocate failed\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+
+ NpFcb->CacheDataSize = 0;
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ NpFcb->CacheFileOffset = 0;
+ NpFcb->CacheDataSize = 0;
+
+ } else if ( NpFcb->CacheType != WriteBehind ) {
+
+ DebugTrace( -1, Dbg, "CacheWrite not writebehind -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+
+ }
+
+ //
+ // If the data is non sequential and non overlapping, flush the
+ // existing cache.
+ //
+
+ if ( NpFcb->CacheDataSize != 0 &&
+ ( FileOffset < NpFcb->CacheFileOffset ||
+ FileOffset > NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) ) {
+
+ //
+ // Release and then AcquireFcbAndFlush() will get us to the front
+ // of the queue before re-acquiring. This avoids potential deadlocks.
+ //
+
+ NwReleaseFcb( NpFcb );
+
+ if ( IrpContext != NULL ) {
+ DebugTrace( 0, Dbg, "Data is not sequential, flushing data\n", 0 );
+
+ status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
+
+ if ( !NT_SUCCESS( status ) ) {
+ ExRaiseStatus( status );
+ }
+
+ }
+
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ return( FALSE );
+
+ }
+
+ //
+ // The data is sequential, see if it fits.
+ //
+
+ if ( SpaceForWriteBehind( NpFcb, FileOffset, BytesToWrite ) ) {
+
+ try {
+
+ RtlCopyMemory(
+ NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
+ UserBuffer,
+ BytesToWrite );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in CacheWrite.\n", 0 );
+ DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return ( FALSE );
+ }
+
+ if ( NpFcb->CacheDataSize <
+ (FileOffset - NpFcb->CacheFileOffset + BytesToWrite) ) {
+
+ NpFcb->CacheDataSize =
+ FileOffset - NpFcb->CacheFileOffset + BytesToWrite;
+
+ }
+
+ DebugTrace(-1, Dbg, "CacheWrite -> TRUE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( TRUE );
+
+ } else if ( IrpContext != NULL ) {
+
+ //
+ // The data didn't fit in the cache. If the cache is empty
+ // then its time to return because it never will fit and we
+ // have no stale data. This can happen if this request or
+ // another being processed in parallel flush the cache and
+ // TryAgain.
+ //
+
+ if ( NpFcb->CacheDataSize == 0 ) {
+ DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ //
+ // The data didn't fit in the cache, flush the cache
+ //
+
+ DebugTrace( 0, Dbg, "Cache is full, flushing data\n", 0 );
+
+ //
+ // We must be at the front of the Queue before writing.
+ //
+
+ NwReleaseFcb( NpFcb );
+
+ status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
+
+ if ( !NT_SUCCESS( status ) ) {
+ ExRaiseStatus( status );
+ }
+
+ //
+ // Now see if it fits in the cache. We need to repeat all
+ // the tests again because two requests can flush the cache at the
+ // same time and the other one of them could have nearly filled it again.
+ //
+
+ goto TryAgain;
+
+ } else {
+ DebugTrace(-1, Dbg, "CacheWrite full -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+}
+
+
+BOOLEAN
+OkToReadAhead(
+ PFCB Fcb,
+ IN ULONG FileOffset,
+ IN UCHAR IoType
+ )
+/*++
+
+Routine Description:
+
+ This routine determines whether the attempted i/o is sequential (so that
+ we can use the cache).
+
+Arguments:
+
+ Fcb - A pointer the the Fcb of the file being read.
+
+ FileOffset - The file offset to read.
+
+Return Value:
+
+ TRUE - The operation is sequential.
+ FALSE - The operation is not sequential.
+
+--*/
+{
+ PAGED_CODE();
+
+ if ( Fcb->NonPagedFcb->CacheType == IoType &&
+ !Fcb->ShareAccess.SharedWrite &&
+ FileOffset == Fcb->LastReadOffset + Fcb->LastReadSize ) {
+
+ DebugTrace(0, Dbg, "Io is sequential\n", 0 );
+ return( TRUE );
+
+ } else {
+
+ DebugTrace(0, Dbg, "Io is not sequential\n", 0 );
+ return( FALSE );
+
+ }
+}
+
+
+ULONG
+CalculateReadAheadSize(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG CacheReadSize,
+ IN ULONG FileOffset,
+ IN ULONG ByteCount
+ )
+/*++
+
+Routine Description:
+
+ This routine determines the amount of data that can be read ahead,
+ and sets up for the read.
+
+ Note: Fcb must be acquired exclusive before calling.
+
+Arguments:
+
+ NpFcb - A pointer the the nonpaged FCB of the file being read.
+
+ FileOffset - The file offset to read.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ ULONG ReadSize;
+ ULONG CacheSize;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CalculateReadAheadSize\n", 0 );
+
+ if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
+ ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
+
+ CacheSize = IrpContext->pNpScb->MaxReceiveSize;
+
+ } else {
+
+ CacheSize = IrpContext->pNpScb->BufferSize;
+
+ }
+
+ if ( OkToReadAhead( NpFcb->Fcb, FileOffset - CacheReadSize, ReadAhead ) &&
+ ByteCount < CacheSize ) {
+
+ ReadSize = CacheSize;
+
+ } else {
+
+ //
+ // Do not read ahead.
+ //
+
+ DebugTrace( 0, Dbg, "No read ahead\n", 0 );
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
+ return ( ByteCount );
+
+ }
+
+ //
+ // Allocate pool for the segment of the read
+ //
+
+ if ( NpFcb->CacheBuffer == NULL ) {
+
+ try {
+
+ NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, ReadSize );
+ NpFcb->CacheSize = ReadSize;
+
+ NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, ReadSize, FALSE, FALSE, NULL );
+ if ( NpFcb->CacheMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( NpFcb->CacheBuffer != NULL) {
+ FREE_POOL( NpFcb->CacheBuffer );
+
+ NpFcb->CacheBuffer = NULL;
+ }
+
+ NpFcb->CacheSize = 0;
+ NpFcb->CacheDataSize = 0;
+
+ DebugTrace( 0, Dbg, "Failed to allocated buffer\n", 0 );
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
+ return( ByteCount );
+ }
+
+ } else {
+ ReadSize = MIN ( NpFcb->CacheSize, ReadSize );
+ }
+
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ReadSize );
+ return( ReadSize );
+}
+
+NTSTATUS
+FlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine flushes the cache buffer for the NpFcb. The caller must
+ have acquired the FCB exclusive prior to making this call!
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the nonpaged FCB of the file to flush.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ if ( NpFcb->CacheDataSize != 0 && NpFcb->CacheType == WriteBehind ) {
+
+ LARGE_INTEGER ByteOffset;
+
+ ByteOffset.QuadPart = NpFcb->CacheFileOffset;
+
+ status = DoWrite(
+ IrpContext,
+ ByteOffset,
+ NpFcb->CacheDataSize,
+ NpFcb->CacheBuffer,
+ NpFcb->CacheMdl );
+
+ //
+ // DoWrite leaves us at the head of the queue. The caller
+ // is responsible for dequeueing the irp context appropriately.
+ //
+
+ if ( NT_SUCCESS( status ) ) {
+ NpFcb->CacheDataSize = 0;
+ }
+ }
+
+ return( status );
+}
+
+NTSTATUS
+AcquireFcbAndFlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine acquires the FCB exclusive and flushes the cache
+ buffer for the acquired NpFcb.
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the nonpaged FCB of the file to flush.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireExclusiveFcb( NpFcb, TRUE );
+
+ status = FlushCache( IrpContext, NpFcb );
+
+ //
+ // Release the FCB and remove ourselves from the queue.
+ // Frequently the caller will want to grab a resource so
+ // we need to be off the queue then.
+ //
+
+ NwReleaseFcb( NpFcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( status );
+}
diff --git a/private/nw/rdr/callback.c b/private/nw/rdr/callback.c
new file mode 100644
index 000000000..7228813ea
--- /dev/null
+++ b/private/nw/rdr/callback.c
@@ -0,0 +1,190 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ callback.c
+
+Abstract:
+
+ This module implements NCP Response callback routines.
+
+Author:
+
+ Manny Weiser [MannyW] 3-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+#ifdef ALLOC_PRAGMA
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, SynchronousResponseCallback )
+#pragma alloc_text( PAGE1, AsynchResponseCallback )
+#endif
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+SynchronousResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ )
+/*++
+
+Routine Description:
+
+ This routine is the callback routine for an NCP which has no
+ return parameters and the caller blocks waiting for a response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ PEPrequest *pNcpHeader;
+ PEPresponse *pNcpResponse;
+
+ DebugTrace( 0, Dbg, "SynchronousResponseCallback\n", 0 );
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+#ifdef MSWDBG
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+ pIrpContext->DebugValue = 0x103;
+#endif
+ pIrpContext->pOriginalIrp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING;
+ NwSetIrpContextEvent( pIrpContext );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ pIrpContext->ResponseLength = BytesAvailable;
+
+ //
+ // Simply copy the data into the response buffer, if it is not
+ // already there (because we used an IRP to receive the data).
+ //
+
+ if ( RspData != pIrpContext->rsp ) {
+ CopyBufferToMdl( pIrpContext->RxMdl, 0, RspData, pIrpContext->ResponseLength );
+ }
+
+ //
+ // Remember the returned error code.
+ //
+
+ pNcpHeader = (PEPrequest *)pIrpContext->rsp;
+ pNcpResponse = (PEPresponse *)(pNcpHeader + 1);
+
+ pIrpContext->ResponseParameters.Error = pNcpResponse->error;
+
+ //
+ // Tell the caller that the response has been received.
+ //
+
+#ifdef MSWDBG
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+ pIrpContext->DebugValue = 0x104;
+#endif
+
+ pIrpContext->pOriginalIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrpContext->pOriginalIrp->IoStatus.Information = BytesAvailable;
+
+ NwSetIrpContextEvent( pIrpContext );
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+AsynchResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ )
+/*++
+
+Routine Description:
+
+ This routine is the callback routine for an NCP which has no
+ return parameters and the caller DOES NOT BLOCK waiting for a
+ response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ Status = STATUS_REMOTE_NOT_LISTENING;
+
+ } else {
+
+ if ( ((PNCP_RESPONSE)RspData)->Status != 0 ) {
+
+ Status = STATUS_LINK_FAILED;
+
+ } else {
+
+ Status = NwErrorToNtStatus( ((PNCP_RESPONSE)RspData)->Error );
+
+ }
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, Status );
+
+ return STATUS_SUCCESS;
+}
+
+
diff --git a/private/nw/rdr/cleanup.c b/private/nw/rdr/cleanup.c
new file mode 100644
index 000000000..2eb22afcd
--- /dev/null
+++ b/private/nw/rdr/cleanup.c
@@ -0,0 +1,591 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ cleanup.c
+
+Abstract:
+
+ This module implements the file cleanup routine for Netware Redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 9-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CLEANUP)
+
+//
+// local procedure prototypes
+//
+
+NTSTATUS
+NwCommonCleanup (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+
+NTSTATUS
+NwCleanupRcb (
+ IN PIRP Irp,
+ IN PRCB Rcb
+ );
+
+NTSTATUS
+NwCleanupScb (
+ IN PIRP Irp,
+ IN PSCB Scb
+ );
+
+NTSTATUS
+NwCleanupIcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PIRP Irp,
+ IN PICB Icb
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdCleanup )
+#pragma alloc_text( PAGE, NwCommonCleanup )
+#pragma alloc_text( PAGE, NwCleanupScb )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwCleanupIcb )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+NwCleanupRcb
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdCleanup (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtCleanupFile API calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object to use.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdCleanup\n", 0);
+
+ //
+ // Call the common cleanup routine.
+ //
+
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ FsRtlEnterFileSystem();
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ status = NwCommonCleanup( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( IrpContext ) {
+ NwCompleteRequest( IrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to our caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdCleanup -> %08lx\n", status );
+ return status;
+}
+
+
+NTSTATUS
+NwCommonCleanup (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for cleaning up a file.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsContext2;
+
+ PAGED_CODE();
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonCleanup\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %08lx\n", (ULONG)IrpContext);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, "FileObject = %08lx\n", (ULONG)irpSp->FileObject);
+
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "The file is disconnected\n", 0);
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonCleanup -> %08lx\n", status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Decide how to handle this IRP.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_RCB: // Cleanup the file system
+
+ status = NwCleanupRcb( Irp, (PRCB)fsContext2 );
+ break;
+
+ case NW_NTC_SCB: // Cleanup the server control block
+
+ status = NwCleanupScb( Irp, (PSCB)fsContext2 );
+ break;
+
+ case NW_NTC_ICB: // Cleanup the remote file
+ case NW_NTC_ICB_SCB: // Cleanup the server
+
+ status = NwCleanupIcb( IrpContext, Irp, (PICB)fsContext2 );
+ break;
+
+#ifdef NWDBG
+ default:
+
+ //
+ // This is not one of ours.
+ //
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+ break;
+#endif
+
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonCleanup -> %08lx\n", status);
+
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwCleanupRcb (
+ IN PIRP Irp,
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up a RCB.
+
+ This routine grabs a spinlock so must not be paged out while running.
+
+ Do not reference the code section since this will start the timer and
+ we don't stop it in the rcb close path.
+
+Arguments:
+
+ Irp - Supplies the IRP associated with the cleanup.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpSp;
+ PFILE_OBJECT closingFileObject;
+ BOOLEAN OwnRcb;
+ BOOLEAN OwnMessageLock = FALSE;
+ KIRQL OldIrql;
+ PLIST_ENTRY listEntry, nextListEntry;
+ PIRP_CONTEXT pTestIrpContext;
+ PIO_STACK_LOCATION pTestIrpSp;
+ PIRP pTestIrp;
+
+ DebugTrace(+1, Dbg, "NwCleanupRcb...\n", 0);
+
+ //
+ // Now acquire exclusive access to the Rcb
+ //
+
+ NwAcquireExclusiveRcb( Rcb, TRUE );
+ OwnRcb = TRUE;
+
+ status = STATUS_SUCCESS;
+
+ try {
+
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ IoRemoveShareAccess( irpSp->FileObject,
+ &Rcb->ShareAccess );
+
+ NwReleaseRcb( Rcb );
+ OwnRcb = FALSE;
+
+ closingFileObject = irpSp->FileObject;
+
+
+ //
+ // Walk the message queue and complete any outstanding Get Message IRPs
+ //
+
+ KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql );
+ OwnMessageLock = TRUE;
+
+ for ( listEntry = NwGetMessageList.Flink;
+ listEntry != &NwGetMessageList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ //
+ // If the file object of the queued request, matches the file object
+ // that is being closed, remove the IRP from the queue, and
+ // complete it with an error.
+ //
+
+ pTestIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+ pTestIrp = pTestIrpContext->pOriginalIrp;
+ pTestIrpSp = IoGetCurrentIrpStackLocation( pTestIrp );
+
+ if ( pTestIrpSp->FileObject == closingFileObject ) {
+ RemoveEntryList( listEntry );
+
+ IoAcquireCancelSpinLock( &pTestIrp->CancelIrql );
+ IoSetCancelRoutine( pTestIrp, NULL );
+ IoReleaseCancelSpinLock( pTestIrp->CancelIrql );
+
+ NwCompleteRequest( pTestIrpContext, STATUS_INVALID_HANDLE );
+ }
+
+ }
+
+ KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
+ OwnMessageLock = FALSE;
+
+ } finally {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( Rcb );
+ }
+
+ if ( OwnMessageLock ) {
+ KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
+ }
+
+ DebugTrace(-1, Dbg, "NwCleanupRcb -> %08lx\n", status);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return status;
+}
+
+
+NTSTATUS
+NwCleanupScb (
+ IN PIRP Irp,
+ IN PSCB Scb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up an ICB.
+
+Arguments:
+
+ Irp - Supplies the IRP associated with the cleanup.
+
+ Scb - Supplies the SCB to cleanup.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCleanupScb...\n", 0);
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Ensure that this SCB is still active.
+ //
+
+ NwVerifyScb( Scb );
+
+ //
+ // Cancel any IO on this SCB.
+ //
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCleanupScb -> %08lx\n", Status);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCleanupIcb (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PIRP Irp,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up an ICB.
+
+Arguments:
+
+ Irp - Supplies the IRP associated with the cleanup.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_FCB NpFcb;
+
+ DebugTrace(+1, Dbg, "NwCleanupIcb...\n", 0);
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ Icb->State = ICB_STATE_CLEANED_UP;
+
+ //
+ // Cancel any IO on this ICB.
+ //
+
+#if 0
+ // HACKHACK
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ PLIST_ENTRY listEntry;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) {
+
+ PIRP_CONTEXT IrpContext;
+
+ IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ if ( IrpContext->Icb == Icb ) {
+
+ PIRP irp = pIrpContext->pOriginalIrp;
+
+ IoAcquireCancelSpinLock( &irp->CancelIrql );
+ IoSetCancelRoutine( irp, NULL );
+ IoReleaseCancelSpinLock( irp->CancelIrql );
+
+ RemoveEntryList( &IrpContext->NextRequest );
+ NwCompleteRequest( IrpContext, STATUS_NOT_SUPPORTED );
+ break;
+ }
+ }
+
+ NwReleaseRcb( &NwRcb );
+ }
+#endif
+
+ //
+ // If this is a remote file clear all the cache garbage.
+ //
+
+ if ( Icb->NodeTypeCode == NW_NTC_ICB ) {
+
+ if ( Icb->HasRemoteHandle ) {
+
+ //
+ // Free all of file lock structures that are still hanging around.
+ //
+
+ pIrpContext->pScb = Icb->SuperType.Fcb->Scb;
+ pIrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb;
+
+ NwFreeLocksForIcb( pIrpContext, Icb );
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ //
+ //
+ // If this is an executable opened over the net, then
+ // its possible that the executables image section
+ // might still be kept open.
+ //
+ // Ask MM to flush the section closed. This will fail
+ // if the executable in question is still running.
+ //
+
+ NpFcb = Icb->SuperType.Fcb->NonPagedFcb;
+ MmFlushImageSection(&NpFcb->SegmentObject, MmFlushForWrite);
+
+ //
+ // There is also a possiblity that there is a user section
+ // open on this file, in which case we need to force the
+ // section closed to make sure that they are cleaned up.
+ //
+
+ MmForceSectionClosed(&NpFcb->SegmentObject, TRUE);
+
+ }
+
+ //
+ // Remove shared access.
+ //
+
+ IoRemoveShareAccess(
+ Icb->FileObject,
+ &Icb->SuperType.Fcb->ShareAccess );
+ }
+
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCleanupIcb -> %08lx\n", Status);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return Status;
+}
+
diff --git a/private/nw/rdr/close.c b/private/nw/rdr/close.c
new file mode 100644
index 000000000..7caddbd04
--- /dev/null
+++ b/private/nw/rdr/close.c
@@ -0,0 +1,571 @@
+/*++
+
+Copyright (c) 1992-4 Microsoft Corporation
+
+Module Name:
+
+ Close.c
+
+Abstract:
+
+ This module implements the File Close routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CLOSE)
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonClose (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwCloseRcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PRCB Rcb
+ );
+
+NTSTATUS
+NwCloseIcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdClose )
+#pragma alloc_text( PAGE, NwCommonClose )
+#pragma alloc_text( PAGE, NwCloseRcb )
+#pragma alloc_text( PAGE, NwCloseIcb )
+#endif
+
+
+NTSTATUS
+NwFsdClose (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of Close.
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdClose\n", 0);
+
+ //
+ // Call the common Close routine
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ Status = NwCommonClose( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdClose -> %08lx\n", Status);
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonClose (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for closing a file.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsContext2;
+
+ PAGED_CODE();
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonClose\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %08lx\n", (ULONG)IrpContext);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, "FileObject = %08lx\n", (ULONG)irpSp->FileObject);
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "The file is disconnected\n", 0);
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonClose -> %08lx\n", status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Decide how to handle this IRP.
+ //
+
+ switch (nodeTypeCode) {
+
+
+ case NW_NTC_RCB: // Close the file system
+
+ status = NwCloseRcb( IrpContext, (PRCB)fsContext2 );
+ status = STATUS_SUCCESS;
+ break;
+
+ case NW_NTC_ICB: // Close the remote file
+ case NW_NTC_ICB_SCB: // Close the SCB
+
+ status = NwCloseIcb( IrpContext, (PICB)fsContext2 );
+ NwDereferenceUnlockableCodeSection ();
+ break;
+
+#ifdef NWDBG
+ default:
+
+ //
+ // This is not one of ours.
+ //
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+ break;
+#endif
+
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ //
+ // Just in-case this handle was the last one before we unload.
+ //
+
+ NwUnlockCodeSections(TRUE);
+
+ DebugTrace(-1, Dbg, "NwCommonClose -> %08lx\n", status);
+
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwCloseRcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up a RCB.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context pointers for this close.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCloseRcb...\n", 0);
+
+ //
+ // Now acquire exclusive access to the Rcb
+ //
+
+ NwAcquireExclusiveRcb( Rcb, TRUE );
+
+ status = STATUS_SUCCESS;
+ --Rcb->OpenCount;
+
+ NwReleaseRcb( Rcb );
+
+ DebugTrace(-1, Dbg, "MsCloseRcb -> %08lx\n", status);
+
+ //
+ // And return to our caller
+ //
+
+ return status;
+}
+
+
+NTSTATUS
+NwCloseIcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up an ICB.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context pointers for this close.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_SCB pNpScb;
+ PVCB Vcb;
+ PFCB Fcb;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCloseIcb...\n", 0);
+
+ ASSERT( Icb->State == ICB_STATE_CLEANED_UP ||
+ Icb->State == ICB_STATE_CLOSE_PENDING );
+
+ //
+ // If this is a remote file close the remote handle.
+ //
+
+ Status = STATUS_SUCCESS;
+ IrpContext->Icb = Icb;
+ Fcb = Icb->SuperType.Fcb;
+
+ if (( Icb->NodeTypeCode == NW_NTC_ICB ) ||
+ ( Icb->NodeTypeCode == NW_NTC_DCB )) {
+
+ pNpScb = Fcb->Scb->pNpScb;
+ IrpContext->pNpScb = pNpScb;
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Vcb = Fcb->Vcb;
+
+ //
+ // Dump the write behind cache.
+ //
+
+ Status = AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ IoRaiseInformationalHardError(
+ STATUS_LOST_WRITEBEHIND_DATA,
+ &Fcb->FullFileName,
+ (PKTHREAD)IrpContext->pOriginalIrp->Tail.Overlay.Thread );
+ }
+
+ //
+ // Is this a print job?
+ // Icb->IsPrintJob will be false if a 16 bit app is
+ // responsible for sending the job.
+ //
+
+ if ( FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) &&
+ Icb->IsPrintJob ) {
+
+ //
+ // Yes, did we print?
+ //
+
+ if ( Icb->ActuallyPrinted ) {
+
+ //
+ // Yes. Send a close file and start queue job NCP
+ //
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_START_JOB,
+ Vcb->Specific.Print.QueueId,
+ Icb->JobId );
+ } else {
+
+ //
+ // No. Cancel the job.
+ //
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB,
+ Vcb->Specific.Print.QueueId,
+ Icb->JobId );
+ }
+
+ } else {
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode != NW_NTC_DCB ) {
+
+ //
+ // No, send a close file NCP.
+ //
+
+ ASSERT( IrpContext->pTdiStruct == NULL );
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ // If this is in the long file name space and
+ // the last access flag has been set, we have to
+ // reset the last access time _after_ closing the file.
+
+ if ( Icb->UserSetLastAccessTime &&
+ BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWD_W_bDbC",
+ NCP_LFN_SET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_SET_INFO_LASTACCESS_DATE,
+ 28,
+ Fcb->LastAccessDate,
+ 8,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Fcb->RelativeFileName );
+ }
+
+ //
+ // If someone set the shareable bit, then
+ // see if we can send the NCP over the wire (all
+ // instances of the file need to be closed).
+ //
+
+ if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE ) ) {
+ LazySetShareable( IrpContext, Icb, Fcb );
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
+ Icb->Handle[0]);
+ }
+
+ }
+
+ Icb->HasRemoteHandle = FALSE;
+ }
+
+ } else {
+
+ pNpScb = Icb->SuperType.Scb->pNpScb;
+ IrpContext->pNpScb = pNpScb;
+ IrpContext->pScb = pNpScb->pScb;
+
+ if ( Icb->HasRemoteHandle ) {
+
+ //
+ // If we have a remote handle this is a file stream ICB. We
+ // need to close the remote handle. The exchange will get us
+ // to the head of the queue to protect the SCB state.
+ //
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ Icb->HasRemoteHandle = FALSE;
+
+ pNpScb->pScb->OpenNdsStreams--;
+
+ ASSERT( pNpScb->pScb->MajorVersion > 3 );
+
+ //
+ // Do we need to unlicense this connection?
+ //
+
+ if ( ( pNpScb->pScb->UserName.Length == 0 ) &&
+ ( pNpScb->pScb->VcbCount == 0 ) &&
+ ( pNpScb->pScb->OpenNdsStreams == 0 ) ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ }
+
+ if ( Icb->Pid != INVALID_PID ) {
+
+ //
+ // This ICB was involved in a search, send the end job,
+ // then free the PID.
+ //
+
+ NwUnmapPid( Icb->Pid, IrpContext );
+ }
+
+ //
+ // Update the time the SCB was last used.
+ //
+
+ KeQuerySystemTime( &pNpScb->LastUsedTime );
+
+ //
+ // Wait the SCB queue. We do this now since NwDeleteIcb may cause
+ // a packet to be sent by this thread (from NwCleanupVcb()) while
+ // holding the RCB. To eliminate this potential source of deadlock,
+ // queue this IrpContext to the SCB queue before acquiring the RCB.
+ //
+ // Also, we mark this IRP context not reconnectable, since the
+ // reconnect logic, will try to acquire the RCB.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Delete the ICB.
+ //
+
+ NwDeleteIcb( IrpContext, Icb );
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwCloseIcb -> %08lx\n", Status);
+ return Status;
+}
diff --git a/private/nw/rdr/const.h b/private/nw/rdr/const.h
new file mode 100644
index 000000000..45e837360
--- /dev/null
+++ b/private/nw/rdr/const.h
@@ -0,0 +1,166 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Const.h
+
+Abstract:
+
+ This module declares the obal data used by the NetWare redirector
+ file system.
+
+Author:
+
+ Colin Watson [ColinW] 14-Jan-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWCONST_
+#define _NWCONST_
+
+// Number of spare stack locations required in Irp's submitted to this
+// filesystem
+
+#define NWRDR_IO_STACKSIZE 2
+
+//
+// NT uses a system time measured in 100 nanosecnd intervals. define
+// convenient constants for setting the timer.
+//
+
+#define MICROSECONDS 10
+#define MILLISECONDS MICROSECONDS*1000
+#define SECONDS MILLISECONDS*1000
+
+#define NwOneSecond 10000000
+
+//
+// Default number of times to retranmit a packet before giving up
+// on waiting for a response.
+//
+
+#define DEFAULT_RETRY_COUNT 10
+
+//
+// Amount of time, in seconds, an idle SCB or VCB should be kept around before
+// being cleaned up.
+//
+
+#define DORMANT_SCB_KEEP_TIME 120
+#define DORMANT_VCB_KEEP_TIME 120
+
+//
+// Largest netware file name
+//
+
+#define NW_MAX_FILENAME_LENGTH 255
+#define NW_MAX_FILENAME_SIZE ( NW_MAX_FILENAME_LENGTH * sizeof(WCHAR) )
+
+//
+// Default frequency for running the scavenger (in 1/18th second ticks)
+// Approx once per minute.
+//
+
+#define DEFAULT_SCAVENGER_TICK_RUN_COUNT 1100
+
+//
+// Size of the drive map table. With room for 26 letter connections,
+// and 10 LPT connections.
+//
+
+#define MAX_DISK_REDIRECTIONS 26
+#define MAX_LPT_REDIRECTIONS 10
+#define DRIVE_MAP_TABLE_SIZE (MAX_DISK_REDIRECTIONS + MAX_LPT_REDIRECTIONS)
+
+//
+// The size of the largest packet we can generate, rounded up to DWORD
+// size. This longest packet is a long name query.
+//
+
+#define MAX_SEND_DATA 256+32
+//
+// The size of the largest non READ packet we can receive, rounded up to DWORD
+// size. This longest packet is read queue job list of 250 jobs
+//
+
+#define MAX_RECV_DATA 544+32
+
+//
+// Best guess at max packet size, if the transport can't tell us.
+// Pick the largest value that will work on any net.
+//
+
+#define DEFAULT_PACKET_SIZE 512
+
+//
+// How close we want to get to true MTU of a connection
+//
+
+#define BURST_PACKET_SIZE_TOLERANCE 8
+
+//
+// Default tick count, in case the transport won't fess up.
+//
+
+#define DEFAULT_TICK_COUNT 2
+
+//
+// Maximum number of times to retry SAP broadcast if we get no response
+//
+
+#define MAX_SAP_RETRIES 2
+
+//
+// The maximum number of SAP response to process if we get many.
+//
+
+#define MAX_SAP_RESPONSES 4
+
+
+#define LFN_NO_OS2_NAME_SPACE -1
+
+//
+// The ordinal for the long namespace in the namespace packet.
+//
+
+#define LONG_NAME_SPACE_ORDINAL 4
+
+//
+// Largest possible SAP Response size and size of a SAP record
+//
+
+#define MAX_SAP_RESPONSE_SIZE 512
+#define SAP_RECORD_SIZE (2 + 48 + 12 + 2)
+#define FIND_NEAREST_RESP_SIZE (2 + SAP_RECORD_SIZE)
+
+//
+// Netware limits
+//
+
+#define MAX_SERVER_NAME_LENGTH 48
+#define MAX_UNICODE_UID_LENGTH 8
+#define MAX_USER_NAME_LENGTH 100 // BUGBUG - What is correct value?
+#define MAX_VOLUME_NAME_LENGTH 16 // BUGBUG - What is correct value?
+
+//
+// Maximum number of unique drive letters we will send to a server.
+// Only seems to matter to portable netWare servers.
+//
+#define MAX_DRIVES 64
+
+
+//
+// Default Timeout Event interval. We do not want to fill up the
+// event-log with timeout events. If a timeout event has been logged
+// in the last timeout event interval, we will ignore further timeout
+// events.
+//
+
+#define DEFAULT_TIMEOUT_EVENT_INTERVAL 5
+
+
+#endif // _NWCONST_
diff --git a/private/nw/rdr/convert.c b/private/nw/rdr/convert.c
new file mode 100644
index 000000000..4caf5372a
--- /dev/null
+++ b/private/nw/rdr/convert.c
@@ -0,0 +1,732 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Convert.c
+
+Abstract:
+
+ This module implements conversion routine to map NT formats to
+ Netware and vice versa.
+
+Author:
+
+ Manny Weiser [MannyW] 3-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+typedef union _NCP_DATE {
+ USHORT Ushort;
+ struct {
+ USHORT Day : 5;
+ USHORT Month : 4;
+ USHORT Year : 7;
+ } Struct;
+} NCP_DATE;
+
+typedef union _NCP_TIME {
+ USHORT Ushort;
+ struct {
+ USHORT TwoSeconds : 5;
+ USHORT Minutes : 6;
+ USHORT Hours : 5;
+ } Struct;
+} NCP_TIME;
+
+#define BASE_DOS_ERROR ((NTSTATUS )0xC0010000L)
+
+
+struct {
+ UCHAR NetError;
+ NTSTATUS ResultingStatus;
+} Error_Map[] = {
+ // NetWare specific error mappings
+ { 1, STATUS_DISK_FULL },
+ {128, STATUS_SHARING_VIOLATION },
+ {129, STATUS_INSUFF_SERVER_RESOURCES },
+ {130, STATUS_ACCESS_DENIED },
+ {131, STATUS_DATA_ERROR },
+ {132, STATUS_ACCESS_DENIED },
+ {133, STATUS_OBJECT_NAME_COLLISION },
+ {134, STATUS_OBJECT_NAME_COLLISION },
+ {135, STATUS_OBJECT_NAME_INVALID },
+ {136, STATUS_INVALID_HANDLE },
+ {137, STATUS_ACCESS_DENIED },
+ {138, STATUS_ACCESS_DENIED },
+ {139, STATUS_ACCESS_DENIED },
+ {140, STATUS_ACCESS_DENIED },
+ {141, STATUS_SHARING_VIOLATION },
+ {142, STATUS_SHARING_VIOLATION },
+ {143, STATUS_ACCESS_DENIED },
+ {144, STATUS_ACCESS_DENIED },
+ {145, STATUS_OBJECT_NAME_COLLISION },
+ {146, STATUS_OBJECT_NAME_COLLISION },
+ {147, STATUS_ACCESS_DENIED },
+ {148, STATUS_ACCESS_DENIED },
+ {149, STATUS_ACCESS_DENIED },
+ {150, STATUS_INSUFF_SERVER_RESOURCES },
+ {151, STATUS_NO_SPOOL_SPACE },
+ {152, STATUS_NO_SUCH_DEVICE },
+ {153, STATUS_DISK_FULL },
+ {154, STATUS_NOT_SAME_DEVICE },
+ {155, STATUS_INVALID_HANDLE },
+ {156, STATUS_OBJECT_PATH_NOT_FOUND },
+ {157, STATUS_INSUFF_SERVER_RESOURCES },
+ {158, STATUS_OBJECT_PATH_INVALID },
+ {159, STATUS_SHARING_VIOLATION },
+ {160, STATUS_DIRECTORY_NOT_EMPTY },
+ {161, STATUS_DATA_ERROR },
+ {162, STATUS_FILE_LOCK_CONFLICT },
+ {165, STATUS_OBJECT_NAME_NOT_FOUND },
+ {191, STATUS_OBJECT_NAME_INVALID }, // Name space not loaded
+ {192, STATUS_ACCESS_DENIED},
+ {193, STATUS_ACCOUNT_RESTRICTION },
+ {194, STATUS_ACCOUNT_RESTRICTION },
+ {195, STATUS_ACCOUNT_DISABLED},
+ {197, STATUS_ACCOUNT_DISABLED },
+ {198, STATUS_ACCESS_DENIED },
+ {211, STATUS_ACCESS_DENIED },
+ {212, STATUS_PRINT_QUEUE_FULL },
+ {213, STATUS_PRINT_CANCELLED },
+ {214, STATUS_ACCESS_DENIED },
+ {215, STATUS_PASSWORD_RESTRICTION },
+ {216, STATUS_PASSWORD_RESTRICTION },
+#ifdef QFE_BUILD
+ {217, STATUS_ACCOUNT_RESTRICTION },
+ {218, STATUS_ACCOUNT_RESTRICTION },
+ {219, STATUS_ACCOUNT_RESTRICTION },
+#else
+ {217, STATUS_CONNECTION_COUNT_LIMIT },
+ {218, STATUS_LOGIN_TIME_RESTRICTION },
+ {219, STATUS_LOGIN_WKSTA_RESTRICTION },
+#endif
+ {220, STATUS_ACCOUNT_DISABLED },
+ {222, STATUS_PASSWORD_EXPIRED },
+ {223, NWRDR_PASSWORD_HAS_EXPIRED },
+ {231, STATUS_REMOTE_SESSION_LIMIT },
+ {236, STATUS_UNEXPECTED_NETWORK_ERROR },
+ {251, STATUS_INVALID_PARAMETER },
+ {252, STATUS_NO_MORE_ENTRIES },
+ {253, STATUS_FILE_LOCK_CONFLICT },
+ {254, STATUS_FILE_LOCK_CONFLICT },
+ {255, STATUS_UNSUCCESSFUL},
+
+ // DOS error mappings
+ //{ ERROR_INVALID_FUNCTION, STATUS_NOT_IMPLEMENTED },
+ { ERROR_FILE_NOT_FOUND, STATUS_NO_SUCH_FILE },
+ { ERROR_PATH_NOT_FOUND, STATUS_OBJECT_PATH_NOT_FOUND },
+ { ERROR_TOO_MANY_OPEN_FILES, STATUS_TOO_MANY_OPENED_FILES },
+ { ERROR_ACCESS_DENIED, STATUS_ACCESS_DENIED },
+ { ERROR_INVALID_HANDLE, STATUS_INVALID_HANDLE },
+ { ERROR_NOT_ENOUGH_MEMORY, STATUS_INSUFFICIENT_RESOURCES },
+ { ERROR_INVALID_ACCESS, STATUS_ACCESS_DENIED },
+ { ERROR_INVALID_DATA, STATUS_DATA_ERROR },
+
+ { ERROR_CURRENT_DIRECTORY, STATUS_DIRECTORY_NOT_EMPTY },
+ { ERROR_NOT_SAME_DEVICE, STATUS_NOT_SAME_DEVICE },
+ { ERROR_NO_MORE_FILES, STATUS_NO_MORE_FILES },
+/* */
+/* These are the universal int 24 mappings for the old INT 24 set of errors */
+/* */
+ { ERROR_WRITE_PROTECT, STATUS_MEDIA_WRITE_PROTECTED},
+ { ERROR_BAD_UNIT, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_NOT_READY, STATUS_DEVICE_NOT_READY },
+ { ERROR_BAD_COMMAND, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_CRC, STATUS_CRC_ERROR },
+ { ERROR_BAD_LENGTH, STATUS_DATA_ERROR },
+ { ERROR_SEEK, STATUS_UNSUCCESSFUL },// ***
+ { ERROR_NOT_DOS_DISK, STATUS_DISK_CORRUPT_ERROR }, //***
+ { ERROR_SECTOR_NOT_FOUND, STATUS_NONEXISTENT_SECTOR },
+ { ERROR_OUT_OF_PAPER, STATUS_DEVICE_PAPER_EMPTY},
+ { ERROR_WRITE_FAULT, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_READ_FAULT, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_GEN_FAILURE, STATUS_UNSUCCESSFUL }, // ***
+/* */
+/* These are the new 3.0 error codes reported through INT 24 */
+/* */
+ { ERROR_SHARING_VIOLATION, STATUS_SHARING_VIOLATION },
+ { ERROR_LOCK_VIOLATION, STATUS_FILE_LOCK_CONFLICT },
+ { ERROR_WRONG_DISK, STATUS_WRONG_VOLUME },
+// { ERROR_FCB_UNAVAILABLE, },
+// { ERROR_SHARING_BUFFER_EXCEEDED, },
+/* */
+/* New OEM network-related errors are 50-79 */
+/* */
+ { ERROR_NOT_SUPPORTED, STATUS_NOT_SUPPORTED },
+ { ERROR_REM_NOT_LIST, STATUS_REMOTE_NOT_LISTENING },
+ { ERROR_DUP_NAME, STATUS_DUPLICATE_NAME },
+ { ERROR_BAD_NETPATH, STATUS_BAD_NETWORK_PATH },
+ { ERROR_NETWORK_BUSY, STATUS_NETWORK_BUSY },
+ { ERROR_DEV_NOT_EXIST, STATUS_DEVICE_DOES_NOT_EXIST },
+ { ERROR_TOO_MANY_CMDS, STATUS_TOO_MANY_COMMANDS },
+ { ERROR_ADAP_HDW_ERR, STATUS_ADAPTER_HARDWARE_ERROR },
+ { ERROR_BAD_NET_RESP, STATUS_INVALID_NETWORK_RESPONSE },
+ { ERROR_UNEXP_NET_ERR, STATUS_UNEXPECTED_NETWORK_ERROR },
+ { ERROR_BAD_REM_ADAP, STATUS_BAD_REMOTE_ADAPTER },
+ { ERROR_PRINTQ_FULL, STATUS_PRINT_QUEUE_FULL },
+ { ERROR_NO_SPOOL_SPACE, STATUS_NO_SPOOL_SPACE },
+ { ERROR_PRINT_CANCELLED, STATUS_PRINT_CANCELLED },
+ { ERROR_NETNAME_DELETED, STATUS_NETWORK_NAME_DELETED },
+ { ERROR_NETWORK_ACCESS_DENIED, STATUS_NETWORK_ACCESS_DENIED },
+ { ERROR_BAD_DEV_TYPE, STATUS_BAD_DEVICE_TYPE },
+ { ERROR_BAD_NET_NAME, STATUS_BAD_NETWORK_NAME },
+ { ERROR_TOO_MANY_NAMES, STATUS_TOO_MANY_NAMES },
+ { ERROR_TOO_MANY_SESS, STATUS_REMOTE_SESSION_LIMIT },
+ { ERROR_SHARING_PAUSED, STATUS_SHARING_PAUSED },
+ { ERROR_REQ_NOT_ACCEP, STATUS_REQUEST_NOT_ACCEPTED },
+ { ERROR_REDIR_PAUSED, STATUS_REDIRECTOR_PAUSED },
+/* */
+/* End of INT 24 reportable errors */
+/* */
+ { ERROR_FILE_EXISTS, STATUS_OBJECT_NAME_COLLISION },
+// { ERROR_DUP_FCB, },
+// { ERROR_CANNOT_MAKE, },
+// { ERROR_FAIL_I24, },
+/* */
+/* New 3.0 network related error codes */
+/* */
+// { ERROR_OUT_OF_STRUCTURES, },
+// { ERROR_ALREADY_ASSIGNED, },
+ { ERROR_INVALID_PASSWORD, STATUS_WRONG_PASSWORD },
+ { ERROR_INVALID_PARAMETER, STATUS_INVALID_PARAMETER },
+ { ERROR_NET_WRITE_FAULT, STATUS_NET_WRITE_FAULT },
+/* */
+/* New error codes for 4.0 */
+/* */
+// { ERROR_NO_PROC_SLOTS, },
+// { ERROR_NOT_FROZEN, },
+// { ERR_TSTOVFL, },
+// { ERR_TSTDUP, },
+// { ERROR_NO_ITEMS, },
+// { ERROR_INTERRUPT, },
+
+// { ERROR_TOO_MANY_SEMAPHORES, },
+// { ERROR_EXCL_SEM_ALREADY_OWNED, },
+// { ERROR_SEM_IS_SET, },
+// { ERROR_TOO_MANY_SEM_REQUESTS, },
+// { ERROR_INVALID_AT_INTERRUPT_TIME, },
+
+// { ERROR_SEM_OWNER_DIED, },
+// { ERROR_SEM_USER_LIMIT, },
+// { ERROR_DISK_CHANGE, },
+// { ERROR_DRIVE_LOCKED, },
+ { ERROR_BROKEN_PIPE, STATUS_PIPE_BROKEN },
+/* */
+/* New error codes for 5.0 */
+/* */
+ //
+ // NOTE: ERROR_OPEN_FAILED is handled specially.
+ //
+
+ //
+ // The mapping of ERROR_OPEN_FAILED is context sensitive. If the
+ // disposition requested in the Open_AndX SMB is FILE_CREATE, this
+ // error means that the file already existed. If the disposition
+ // is FILE_OPEN, it means that the file does NOT exist!
+ //
+
+ { ERROR_OPEN_FAILED, STATUS_OPEN_FAILED },
+// { ERROR_BUFFER_OVERFLOW, },
+ { ERROR_DISK_FULL, STATUS_DISK_FULL },
+// { ERROR_NO_MORE_SEARCH_HANDLES, },
+// { ERROR_INVALID_TARGET_HANDLE, },
+// { ERROR_PROTECTION_VIOLATION, STATUS_ACCESS_VIOLATION },
+// { ERROR_VIOKBD_REQUEST, },
+// { ERROR_INVALID_CATEGORY, },
+// { ERROR_INVALID_VERIFY_SWITCH, },
+// { ERROR_BAD_DRIVER_LEVEL, },
+// { ERROR_CALL_NOT_IMPLEMENTED, },
+ { ERROR_SEM_TIMEOUT, STATUS_IO_TIMEOUT },
+ { ERROR_INSUFFICIENT_BUFFER, STATUS_BUFFER_TOO_SMALL },
+ { ERROR_INVALID_NAME, STATUS_OBJECT_NAME_INVALID },
+ { ERROR_INVALID_LEVEL, STATUS_INVALID_LEVEL },
+// { ERROR_NO_VOLUME_LABEL, },
+
+/* NOTE: DosQFSInfo no longer returns the above error; it is still here for */
+/* api\d_qfsinf.asm. */
+
+// { ERROR_MOD_NOT_FOUND, },
+// { ERROR_PROC_NOT_FOUND, },
+
+// { ERROR_WAIT_NO_CHILDREN, },
+
+// { ERROR_CHILD_NOT_COMPLETE, },
+
+// { ERROR_DIRECT_ACCESS_HANDLE, },
+ /* for direct disk access */
+ /* handles */
+// { ERROR_NEGATIVE_SEEK, },
+ /* with negitive offset */
+// { ERROR_SEEK_ON_DEVICE, },
+ /* on device or pipe */
+ { ERROR_BAD_PATHNAME, STATUS_OBJECT_PATH_INVALID }, //*
+
+/*
+ * Error codes 230 - 249 are reserved for MS Networks
+ */
+ { ERROR_BAD_PIPE, STATUS_INVALID_PARAMETER },
+ { ERROR_PIPE_BUSY, STATUS_PIPE_NOT_AVAILABLE },
+ { ERROR_NO_DATA, STATUS_PIPE_EMPTY },
+ { ERROR_PIPE_NOT_CONNECTED, STATUS_PIPE_DISCONNECTED },
+ { ERROR_MORE_DATA, STATUS_BUFFER_OVERFLOW },
+
+ { ERROR_VC_DISCONNECTED, STATUS_VIRTUAL_CIRCUIT_CLOSED },
+};
+
+#define NUM_ERRORS sizeof(Error_Map) / sizeof(Error_Map[0])
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CONVERT)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NtToNwShareFlags )
+#pragma alloc_text( PAGE, NtAttributesToNwAttributes )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, pNwErrorToNtStatus )
+#pragma alloc_text( PAGE1, NwBurstResultToNtStatus )
+#pragma alloc_text( PAGE1, NwConnectionStatusToNtStatus )
+#pragma alloc_text( PAGE1, NwDateTimeToNtTime )
+#pragma alloc_text( PAGE1, NwNtTimeToNwDateTime )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+UCHAR
+NtToNwShareFlags(
+ ULONG DesiredAccess,
+ ULONG NtShareFlags
+ )
+/*++
+
+Routine Description:
+
+ This routine maps a NT desired/share access to Netware share flag bits.
+
+Arguments:
+
+ DesiredAccess - Desired access for open as specified in the read IRP.
+ NtShareFlags - The NT share flags from the create IRP.
+
+Return Value:
+
+ Netware share mode.
+
+--*/
+{
+ UCHAR NwShareFlags = 0;
+ ULONG lDesiredAccess;
+
+ PAGED_CODE();
+
+ //
+ // Ignore share delete, since we can't do anything with it.
+ //
+
+ switch ( NtShareFlags & (FILE_SHARE_READ | FILE_SHARE_WRITE) ) {
+
+ case 0:
+ NwShareFlags = NW_OPEN_EXCLUSIVE;
+ break;
+
+ case FILE_SHARE_READ:
+ NwShareFlags = NW_DENY_WRITE;
+ break;
+
+ case FILE_SHARE_WRITE:
+ NwShareFlags = NW_DENY_READ;
+ break;
+
+ case FILE_SHARE_WRITE | FILE_SHARE_READ:
+ NwShareFlags = 0;
+
+ }
+
+ //
+ // Treat append the same as write.
+ //
+
+ if ( DesiredAccess & FILE_APPEND_DATA) {
+
+ lDesiredAccess = DesiredAccess | FILE_WRITE_DATA;
+
+ } else {
+
+ lDesiredAccess = DesiredAccess;
+
+ }
+
+ switch ( lDesiredAccess & (FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA) ) {
+
+ case (FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA):
+ case (FILE_EXECUTE | FILE_WRITE_DATA):
+ NwShareFlags |= NW_OPEN_EXCLUSIVE | NW_OPEN_FOR_WRITE | NW_OPEN_FOR_READ;
+ break;
+
+ case (FILE_EXECUTE | FILE_READ_DATA):
+ case (FILE_EXECUTE):
+ NwShareFlags |= NW_OPEN_EXCLUSIVE | NW_OPEN_FOR_READ;
+ break;
+
+ case (FILE_WRITE_DATA | FILE_READ_DATA):
+ NwShareFlags |= NW_OPEN_FOR_WRITE | NW_OPEN_FOR_READ;
+ break;
+
+ case (FILE_WRITE_DATA):
+ NwShareFlags |= NW_OPEN_FOR_WRITE;
+ break;
+
+ default:
+ NwShareFlags |= NW_OPEN_FOR_READ;
+ break;
+ }
+
+ if (NwShareFlags & NW_OPEN_EXCLUSIVE) {
+
+ //
+ // Remove the NW_DENY_* flags if exclusive is already specified since
+ // this interferes with the shareable flag.
+ //
+
+ return( NwShareFlags & ~(NW_DENY_READ | NW_DENY_WRITE) );
+ }
+
+ return( NwShareFlags );
+}
+
+
+UCHAR
+NtAttributesToNwAttributes(
+ ULONG FileAttributes
+ )
+/*++
+
+Routine Description:
+
+ This routine maps a NT attributes mask to a Netware mask.
+
+Arguments:
+
+ DesiredAccess - Desired access for open as specified in the read IRP.
+
+Return Value:
+
+ Netware share mode.
+
+--*/
+{
+ return( (UCHAR)FileAttributes & 0x3F );
+}
+
+NTSTATUS
+pNwErrorToNtStatus(
+ UCHAR NwError
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a Netware error code to an NT status code.
+
+Arguments:
+
+ NwError - The netware error.
+
+Return Value:
+
+ NTSTATUS - The converted status.
+
+--*/
+
+{
+ int i;
+
+ ASSERT(NwError != 0);
+
+ //
+ // Errors 2 through 127 are mapped as DOS errors.
+ //
+
+ if ( NwError > 1 && NwError < 128 ) {
+ return( BASE_DOS_ERROR + NwError );
+ }
+
+ //
+ // For other errors, search the table for the matching error number.
+ //
+
+ for ( i = 0; i < NUM_ERRORS; i++ ) {
+ if ( Error_Map[i].NetError == NwError ) {
+ return( Error_Map[i].ResultingStatus );
+ }
+ }
+
+ DebugTrace( 0, 0, "No error mapping for error %d\n", NwError );
+
+#ifdef NWDBG
+ Error( EVENT_NWRDR_NETWORK_ERROR, (NTSTATUS)0xC0010000 | NwError, NULL, 0, 0 );
+#endif
+
+ return( (NTSTATUS)0xC0010000 | NwError );
+}
+
+NTSTATUS
+NwBurstResultToNtStatus(
+ ULONG Result
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a Netware burst result code to an NT status code.
+
+Arguments:
+
+ Result - The netware burst result.
+
+Return Value:
+
+ NTSTATUS - The converted status.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // the 3 high order bits should not be set. but if they are,
+ // we return an error.
+ //
+ if (Result & 0xFFFFFF00)
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+
+ switch ( Result ) {
+
+ case 0:
+ case 3: // No data
+ Status = STATUS_SUCCESS;
+ break;
+
+ case 1:
+ Status = STATUS_DISK_FULL;
+ break;
+
+ case 2: // I/O error
+ Status = STATUS_UNEXPECTED_IO_ERROR;
+ break;
+
+ default:
+ Status = NwErrorToNtStatus( (UCHAR)Result );
+ break;
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+NwConnectionStatusToNtStatus(
+ UCHAR NwStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a Netware connection status code to an NT
+ status code.
+
+Arguments:
+
+ NwStatus - The netware connection status.
+
+Return Value:
+
+ NTSTATUS - The converted status.
+
+--*/
+
+{
+ if ( (NwStatus & 1) == 0 ) {
+ return STATUS_SUCCESS;
+ } else {
+ return STATUS_REMOTE_DISCONNECT;
+ }
+}
+
+LARGE_INTEGER
+NwDateTimeToNtTime (
+ IN USHORT UDate,
+ IN USHORT UTime
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an NCP time to an NT time structure.
+
+Arguments:
+
+ Time - Supplies the time of day to convert
+ Date - Supplies the day of the year to convert
+
+Return Value:
+
+ LARGE_INTEGER - Time structure describing input time.
+
+--*/
+
+{
+ TIME_FIELDS TimeFields;
+ LARGE_INTEGER OutputTime;
+ NCP_DATE Date = *(NCP_DATE *)&UDate;
+ NCP_TIME Time = *(NCP_TIME *)&UTime;
+
+ if ( Date.Ushort == 0 && Time.Ushort == 0 ) {
+
+ //
+ // The file time stamp is zero. Do not return a file time of
+ // zero, since this will be biased to a negative time (due to
+ // time zone fixup), and no one will be able to display it
+ // correctly. Instead, we "randomly" pick Jan 01, 1980 @ 12:00am
+ // as the file time.
+ //
+ // We assume that the netware server is in our time zone.
+
+ RtlSecondsSince1980ToTime(0, &OutputTime);
+
+ } else {
+
+ TimeFields.Year = Date.Struct.Year + (USHORT )1980;
+ TimeFields.Month = Date.Struct.Month;
+ TimeFields.Day = Date.Struct.Day;
+
+ TimeFields.Hour = Time.Struct.Hours;
+ TimeFields.Minute = Time.Struct.Minutes;
+ TimeFields.Second = Time.Struct.TwoSeconds*(USHORT )2;
+ TimeFields.Milliseconds = 0;
+
+ //
+ // Make sure that the times specified in the packet are reasonable
+ // before converting them.
+ //
+
+ if (TimeFields.Year < 1601) {
+ TimeFields.Year = 1601;
+ }
+
+ if (TimeFields.Month > 12) {
+ TimeFields.Month = 12;
+ }
+
+ if (TimeFields.Hour >= 24) {
+ TimeFields.Hour = 23;
+ }
+
+ if (TimeFields.Minute >= 60) {
+ TimeFields.Minute = 59;
+ }
+
+ if (TimeFields.Second >= 60) {
+ TimeFields.Second = 59;
+ }
+
+ if (!RtlTimeFieldsToTime(&TimeFields, &OutputTime)) {
+
+ OutputTime.QuadPart = 0;
+ return OutputTime;
+ }
+
+ }
+
+ // Convert to UTC for the system.
+ ExLocalTimeToSystemTime(&OutputTime, &OutputTime);
+ return OutputTime;
+
+}
+
+NTSTATUS
+NwNtTimeToNwDateTime (
+ IN LARGE_INTEGER NtTime,
+ IN PUSHORT NwDate,
+ IN PUSHORT NwTime
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an NT time structure to an NCP time.
+
+Arguments:
+
+ NtTime - Supplies to NT Time to convert.
+
+ NwDate - Returns the Netware format date.
+
+ NwTime - Returns the Netware format time.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ TIME_FIELDS TimeFields;
+ NCP_DATE Date;
+ NCP_TIME Time;
+
+ if (NtTime.QuadPart == 0) {
+
+ Time.Ushort = Date.Ushort = 0;
+
+ } else {
+
+ LARGE_INTEGER LocalTime;
+
+ // We assume that the netware server is in our time zone.
+
+ ExSystemTimeToLocalTime( &NtTime, &LocalTime );
+ RtlTimeToTimeFields( &LocalTime, &TimeFields );
+
+ if (TimeFields.Year < 1980 || TimeFields.Year > (1980 + 127) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Date.Struct.Year = (USHORT )(TimeFields.Year - 1980);
+ Date.Struct.Month = TimeFields.Month;
+ Date.Struct.Day = TimeFields.Day;
+
+ Time.Struct.Hours = TimeFields.Hour;
+ Time.Struct.Minutes = TimeFields.Minute;
+
+ //
+ // When converting from a higher granularity time to a lesser
+ // granularity time (seconds to 2 seconds), always round up
+ // the time, don't round down.
+ //
+
+ Time.Struct.TwoSeconds = TimeFields.Second / 2;
+
+ }
+
+ *NwDate = *( USHORT *)&Date;
+ *NwTime = *( USHORT *)&Time;
+ return( STATUS_SUCCESS );
+}
+
diff --git a/private/nw/rdr/convert.h b/private/nw/rdr/convert.h
new file mode 100644
index 000000000..e093e4d2c
--- /dev/null
+++ b/private/nw/rdr/convert.h
@@ -0,0 +1,33 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Convert.h
+
+Abstract:
+
+ This module declares the types used to permit exchange.c to be used
+ with minimal change in the NetWare file system.
+
+Author:
+
+ Colin Watson [ColinW] 23-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _CONVERT_
+#define _CONVERT_
+
+#define byte UCHAR
+#define word USHORT
+#define dword ULONG
+
+#define offsetof(r,f) ((size_t)&(((r*)0)->f))
+#define byteswap(x) ((x>>8)+((x&0xFF)<<8))
+
+#endif //_CONVERT_
diff --git a/private/nw/rdr/create.c b/private/nw/rdr/create.c
new file mode 100644
index 000000000..e50c9ad68
--- /dev/null
+++ b/private/nw/rdr/create.c
@@ -0,0 +1,3398 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Create.c
+
+Abstract:
+
+ This module implements the File Create routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+ Manny Weiser [MannyW] 15-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+NTSTATUS
+NwCommonCreate (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+IO_STATUS_BLOCK
+OpenRedirector(
+ IN PIRP_CONTEXT IrpContext,
+ ULONG DesiredAccess,
+ ULONG ShareAccess,
+ PFILE_OBJECT FileObject
+ );
+
+IO_STATUS_BLOCK
+CreateRemoteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING DriveName
+ );
+
+IO_STATUS_BLOCK
+ChangeDirectory(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ );
+
+IO_STATUS_BLOCK
+CreateDir(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ );
+
+NTSTATUS
+FileOrDirectoryExists(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ PUNICODE_STRING Name,
+ OUT PBOOLEAN IsAFile
+ );
+
+IO_STATUS_BLOCK
+OpenFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE SearchFlags,
+ IN BYTE ShareFlags
+ );
+
+IO_STATUS_BLOCK
+CreateNewFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE SearchFlags,
+ IN BYTE ShareFlags
+ );
+
+IO_STATUS_BLOCK
+CreateOrOverwriteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE CreateAttributes,
+ IN BYTE OpenFlags,
+ IN BOOLEAN CreateOperation
+ );
+
+IO_STATUS_BLOCK
+OpenRenameTarget(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PDCB Dcb,
+ IN PICB* Icb
+ );
+
+IO_STATUS_BLOCK
+CreatePrintJob(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb,
+ PUNICODE_STRING DriveName
+ );
+
+VOID
+CloseFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB pIcb
+ );
+
+
+// BUGBUG HACK HACK
+
+BOOLEAN
+MmDisableModifiedWriteOfSection (
+ IN PSECTION_OBJECT_POINTERS SectionObjectPointer
+ );
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdCreate )
+#pragma alloc_text( PAGE, NwCommonCreate )
+#pragma alloc_text( PAGE, ReadAttachEas )
+#pragma alloc_text( PAGE, OpenRedirector )
+#pragma alloc_text( PAGE, CreateRemoteFile )
+#pragma alloc_text( PAGE, ChangeDirectory )
+#pragma alloc_text( PAGE, CreateDir )
+#pragma alloc_text( PAGE, FileOrDirectoryExists )
+#pragma alloc_text( PAGE, OpenFile )
+#pragma alloc_text( PAGE, CreateNewFile )
+#pragma alloc_text( PAGE, CreateOrOverwriteFile )
+#pragma alloc_text( PAGE, OpenRenameTarget )
+#pragma alloc_text( PAGE, CreatePrintJob )
+#pragma alloc_text( PAGE, CloseFile )
+#endif
+
+
+NTSTATUS
+NwFsdCreate (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtCreateFile and NtOpenFile
+ API calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the redirector.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ TimerStart(Dbg);
+ DebugTrace(+1, Dbg, "NwFsdCreate\n", 0);
+
+ //
+ // Call the common create routine, with block allowed if the operation
+ // is synchronous.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ Status = NwCommonCreate( IrpContext );
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( IrpContext ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdCreate -> %08lx\n", Status );
+
+ TimerStop(Dbg,"NwFsdCreate");
+
+ return Status;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+}
+
+
+NTSTATUS
+NwCommonCreate (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for creating/opening a file called by
+ both the fsd and fsp threads.
+
+Arguments:
+
+ IrpContext - Supplies the context information for the IRP to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+
+{
+ IO_STATUS_BLOCK Iosb;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ PFILE_OBJECT FileObject;
+ ACCESS_MASK DesiredAccess;
+ USHORT ShareAccess;
+ ULONG Options;
+ BOOLEAN CreateTreeConnection;
+ BOOLEAN DeleteOnClose;
+ BOOLEAN DeferredLogon;
+ BOOLEAN DereferenceCodeSection = FALSE;
+ BOOLEAN OpenedTreeHandle = FALSE;
+
+ UNICODE_STRING CreateFileName;
+ UNICODE_STRING Drive;
+ UNICODE_STRING Server;
+ UNICODE_STRING Volume;
+ UNICODE_STRING Path;
+ UNICODE_STRING FileName;
+ UNICODE_STRING UserName, Password;
+ ULONG ShareType;
+ WCHAR DriveLetter;
+ DWORD dwExtendedCreate = FALSE;
+
+ PSCB Scb = NULL;
+ PICB Icb;
+ UNICODE_STRING DefaultServer;
+
+ PAGED_CODE();
+
+ //
+ // Get the current IRP stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonCreate\n", 0 );
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp );
+ DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags );
+ DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject );
+ DebugTrace( 0, Dbg, " ->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject );
+ DebugTrace( 0, Dbg, " ->FileName = \"%wZ\"\n", &IrpSp->FileObject->FileName );
+ DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
+ DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
+ DebugTrace( 0, Dbg, "->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
+ DebugTrace( 0, Dbg, "->IrpSp->Flags = %08lx\n", IrpSp->Flags );
+ DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
+ DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options );
+ DebugTrace( 0, Dbg, "->Disposition = %08lx\n", (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff);
+ DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes );
+ DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess );
+ DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
+
+ CreateFileName = IrpSp->FileObject->FileName;
+ Options = IrpSp->Parameters.Create.Options;
+ DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+ ShareAccess = IrpSp->Parameters.Create.ShareAccess;
+
+ CreateTreeConnection = BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION );
+ DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
+
+ DefaultServer.Buffer = NULL;
+
+ //
+ // Make sure the input large integer is valid
+ //
+
+ if (Irp->Overlay.AllocationSize.HighPart != 0) {
+
+ DebugTrace(-1, Dbg, "NwCommonCreate -> STATUS_INVALID_PARAMETER\n", 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Fail requests that don't have the proper impersonation level.
+ //
+
+ if ( IrpSp->Parameters.Create.SecurityContext ) {
+
+ if ( IrpSp->Parameters.Create.SecurityContext->SecurityQos ) {
+
+ if ( IrpSp->Parameters.Create.SecurityContext->SecurityQos->ImpersonationLevel <
+ SecurityImpersonation ) {
+
+ DebugTrace(-1, Dbg, "NwCommonCreate -> Insufficient impersation level.\n", 0);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ Iosb.Status = STATUS_SUCCESS;
+
+ FileObject = IrpSp->FileObject;
+ IrpContext->pNpScb = NULL;
+ IrpContext->Specific.Create.UserUid =
+ GetUid(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ try {
+
+ if ( IrpSp->FileObject->RelatedFileObject != NULL ) {
+
+ //
+ // If we open a handle then the DereferenceCodeSection flag
+ // will be set to false. The dereference will eventually
+ // happen when the file is closed.
+ //
+
+ NwReferenceUnlockableCodeSection();
+ DereferenceCodeSection = TRUE;
+
+ //
+ // Record the relative file name for this open.
+ //
+
+ IrpContext->Specific.Create.FullPathName = CreateFileName;
+
+ Iosb = CreateRemoteFile( IrpContext, NULL );
+
+ //
+ // If we succeeded, we want to keep the code section
+ // referenced because we have opened a handle.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ DereferenceCodeSection = FALSE;
+ }
+
+ try_return( Iosb.Status );
+ }
+
+ Iosb.Status = CrackPath (
+ &CreateFileName,
+ &Drive,
+ &DriveLetter,
+ &Server,
+ &Volume,
+ &Path,
+ &FileName,
+ NULL );
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+ try_return(Iosb.Status);
+ }
+
+ //
+ // Remember this good info.
+ //
+
+ IrpContext->Specific.Create.VolumeName = Volume;
+ IrpContext->Specific.Create.PathName = Path;
+ IrpContext->Specific.Create.DriveLetter = DriveLetter;
+ IrpContext->Specific.Create.FileName = FileName;
+ IrpContext->Specific.Create.FullPathName = CreateFileName;
+
+ RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL );
+
+ //
+ // For now assume default username and password
+ //
+
+ ShareType = RESOURCETYPE_ANY;
+ RtlInitUnicodeString( &UserName, NULL );
+ RtlInitUnicodeString( &Password, NULL );
+
+ if ( Server.Length == 0) {
+
+ //
+ // Opened the redirector itself
+ //
+
+ Iosb = OpenRedirector(
+ IrpContext,
+ DesiredAccess,
+ ShareAccess,
+ FileObject );
+
+ } else if ( Server.Length == Volume.Length - sizeof( WCHAR ) ) {
+
+ if (IpxHandle == 0 ) {
+
+ //
+ // We're not bound to the transport and the user is not
+ // opening the redirector to tell us to bind so return failed.
+ //
+
+ try_return( Iosb.Status = STATUS_REDIRECTOR_NOT_STARTED );
+ }
+
+ NwReferenceUnlockableCodeSection();
+ DereferenceCodeSection = TRUE;
+
+ //
+ // If the only requested access is FILE_LIST_DIRECTORY,
+ // defer the logon. This will allow all CreateScb to
+ // succeed with when the user or password is invalid, so
+ // that the user can see volumes, or enumerate servers
+ // on the server.
+ //
+
+ if ( (DesiredAccess & ~( FILE_LIST_DIRECTORY | SYNCHRONIZE ) ) == 0 ) {
+ DeferredLogon = TRUE;
+ } else {
+ DeferredLogon = FALSE;
+ }
+
+ //
+ // Server = "Server", Volume = "\Server"
+ //
+
+ if ( Server.Length == sizeof(WCHAR) && Server.Buffer[0] == L'*') {
+
+ //
+ // Attempt to open \\*, open a handle to the preferred
+ // server
+ //
+
+ PLOGON Logon;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ Logon = FindUser( &IrpContext->Specific.Create.UserUid, FALSE);
+ ASSERT( Logon != NULL );
+
+ //
+ // Capture the name to avoid holding Rcb or referencing
+ // the logon structure.
+ //
+
+ Iosb.Status = DuplicateUnicodeStringWithString (
+ &DefaultServer,
+ &Logon->ServerName,
+ PagedPool);
+
+ NwReleaseRcb( &NwRcb );
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+ try_return( Iosb.Status );
+ }
+
+ //
+ // If the user specified a preferred server and we managed
+ // to capture the name, try and connect to it.
+ //
+
+ if (DefaultServer.Length != 0) {
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &DefaultServer,
+ NULL,
+ NULL,
+ NULL,
+ DeferredLogon,
+ FALSE );
+
+ } else {
+
+ //
+ // Record that we could not get to the server specified
+ // in the login structure and that we should attempt to
+ // use the nearest server.
+ //
+
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+
+ PNONPAGED_SCB NpScb;
+
+ //
+ // First dequeue the IRP context, in case it was left
+ // on an SCB queue.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ //
+ // Cannot get to the Preferred server so use any
+ // server we have a connection to.
+ //
+
+
+ NpScb = SelectConnection( NULL );
+
+ if (NpScb != NULL ) {
+
+ Scb = NpScb->pScb;
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &NpScb->ServerName,
+ NULL,
+ NULL,
+ NULL,
+ DeferredLogon,
+ FALSE );
+
+ //
+ // Release the SCB reference we obtained from
+ // SelectConnection().
+ //
+
+ NwDereferenceScb( NpScb );
+ }
+ }
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+
+ //
+ // First dequeue the IRP context, in case it was left
+ // on an SCB queue.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ //
+ // Let CreateScb try and find a nearest server to talk
+ // to.
+ //
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ DeferredLogon,
+ FALSE );
+ }
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+ try_return( Iosb.Status );
+ }
+
+ } else {
+
+ //
+ // On handle opens to a server or tree we support the concept
+ // of an open with supplemental credentials. In this case, we return
+ // a handle to the server or a dir server using the provided
+ // credentials regardless of whether or not there are existing
+ // connections to the resource. This is primarily for admin
+ // tools like OleDs.
+ //
+
+ ReadAttachEas( Irp, &UserName, &Password, &ShareType, &dwExtendedCreate );
+
+ if ( dwExtendedCreate ) {
+ ASSERT( UserName.Length > 0 );
+ IrpContext->Specific.Create.fExCredentialCreate = TRUE;
+ IrpContext->Specific.Create.puCredentialName = &UserName;
+ }
+
+ //
+ // Attempt to open \\server
+ //
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &Server,
+ NULL,
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ) ||
+ ( Iosb.Status == STATUS_BAD_NETWORK_PATH ) ||
+ ( Iosb.Status == STATUS_UNSUCCESSFUL ) ) {
+
+ //
+ // If we couldn't find the server or something
+ // inexplicable occurred, attempt to open \\tree.
+ //
+
+ Iosb.Status = NdsCreateTreeScb( IrpContext,
+ &Scb, // dest scb
+ &Server, // tree we want
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ try_return( Iosb.Status );
+ }
+
+ OpenedTreeHandle = TRUE;
+
+ } else if ( !NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // If we failed to get the bindery server for
+ // some legitimate reason, bail out now.
+ //
+ try_return( Iosb.Status );
+ }
+
+ //
+ // We must have a connection at this point. We don't tree
+ // connect the dir server since it's virtual.
+ //
+
+ if ( !OpenedTreeHandle && CreateTreeConnection && !DeleteOnClose ) {
+ TreeConnectScb( Scb );
+ }
+
+ }
+
+ //
+ // Now create the ICB.
+ //
+
+ ASSERT( Iosb.Status == STATUS_SUCCESS );
+ ASSERT( Scb != NULL );
+
+ Icb = NwCreateIcb( NW_NTC_ICB_SCB, Scb );
+ Icb->FileObject = FileObject;
+ NwSetFileObject( FileObject, NULL, Icb );
+
+ //
+ // Indicate that the SCB was opened.
+ //
+
+ Icb->State = ICB_STATE_OPENED;
+
+ //
+ // Is this a tree handle?
+ //
+
+ Icb->IsTreeHandle = OpenedTreeHandle;
+
+ } else {
+
+ NwReferenceUnlockableCodeSection();
+ DereferenceCodeSection = TRUE;
+
+ DeferredLogon = FALSE;
+
+ if ( CreateTreeConnection ) {
+
+ //
+ // We ignore the extended create attribute here because
+ // we DO NOT support extended credential creates to random
+ // files and directories!
+ //
+
+ ReadAttachEas( Irp, &UserName, &Password, &ShareType, NULL );
+
+ if ( DeleteOnClose ) {
+
+ //
+ // Opening a directory to delete a volume. Do not
+ // force logon.
+ //
+
+ DeferredLogon = TRUE;
+ }
+ }
+
+ IrpContext->Specific.Create.ShareType = ShareType;
+ IrpContext->Specific.Create.NdsCreate = FALSE;
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &Server,
+ NULL,
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ||
+ Iosb.Status == STATUS_BAD_NETWORK_PATH ||
+ Iosb.Status == STATUS_UNSUCCESSFUL ) {
+
+ //
+ // If we couldn't find the server or something
+ // inexplicable occurred, attempt to open \\tree.
+ //
+
+ IrpContext->Specific.Create.NdsCreate = TRUE;
+ IrpContext->Specific.Create.NeedNdsData = TRUE;
+
+ Iosb.Status = NdsCreateTreeScb( IrpContext,
+ &Scb,
+ &Server,
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+ }
+
+ //
+ // If we have success, then there's a volume to connect.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+
+ NTSTATUS CreateScbStatus;
+
+ ASSERT( Scb != NULL );
+
+ //
+ // Remember the status from create SCB, since it might
+ // be an interesting warning.
+ //
+
+ CreateScbStatus = Iosb.Status;
+
+ //
+ // We catch this exception in case we have to retry the
+ // create on the NDS path. This sucks, as does the
+ // exception structure in this code right now, but it's
+ // legacy and now is not the time to change it.
+ //
+
+ try {
+
+ Iosb = CreateRemoteFile(
+ IrpContext,
+ &Drive );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Iosb.Status = GetExceptionCode();
+ }
+
+ //
+ // If this is a server whose name is the same as the tree
+ // that it is a member of, and the create was marked as
+ // non-nds and it failed, retry an nds create.
+ //
+
+ if ( ( !NT_SUCCESS( Iosb.Status) ) &&
+ ( !(IrpContext->Specific.Create.NdsCreate) ) &&
+ ( RtlEqualUnicodeString( &(Scb->pNpScb->ServerName),
+ &(Scb->NdsTreeName),
+ TRUE ) ) ) {
+
+ IrpContext->Specific.Create.NdsCreate = TRUE;
+ IrpContext->Specific.Create.NeedNdsData = TRUE;
+
+ Iosb = CreateRemoteFile(
+ IrpContext,
+ &Drive );
+
+ //
+ // If this fails, it will raise status before setting IOSB
+ // and we'll return the status from the original create,
+ // which is the more interesting one.
+ //
+
+ }
+
+ //
+ // If we successfully open the remote file, return the
+ // CreateScb status instead.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = CreateScbStatus;
+ }
+
+ }
+ }
+
+ //
+ // If we succeeded, we want to keep the code section
+ // referenced because we have opened a handle.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ DereferenceCodeSection = FALSE;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+
+ //
+ // Track the Scb in the IrpContext, not in the local Scb
+ // variable since we may have been routed to another server
+ // in process.
+ //
+
+ if ( Scb != NULL ) {
+ ASSERT( IrpContext->pNpScb );
+ NwDereferenceScb( IrpContext->pNpScb );
+ }
+
+ if ( DefaultServer.Buffer != NULL ) {
+ FREE_POOL( DefaultServer.Buffer );
+ }
+
+ DebugTrace(-1, Dbg, "NwCommonCreate -> %08lx\n", Iosb.Status);
+
+ if ( DereferenceCodeSection ) {
+ NwDereferenceUnlockableCodeSection ();
+ }
+
+ }
+
+ //
+ // Map a timeout error to server not found, so that MPR will
+ // try to connect on the next network provider instead of giving up,
+ // which is wrong wrong wrong.
+ //
+
+ if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ) {
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // Map an unbound transport error to server not found, so that MPR
+ // will try to connect on the next provider.
+ //
+
+ if ( Iosb.Status == STATUS_NETWORK_UNREACHABLE ) {
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ return Iosb.Status;
+}
+
+
+NTSTATUS
+ReadAttachEas(
+ IN PIRP Irp,
+ OUT PUNICODE_STRING UserName,
+ OUT PUNICODE_STRING Password,
+ OUT PULONG ShareType,
+ OUT PDWORD CredentialExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes the EAs provided when the caller attempts
+ to attach to a remote server.
+
+ Note: This routine does not create additional storage for the names.
+ It is the callers responsibility to save them if required.
+
+Arguments:
+
+ Irp - Supplies all the information
+
+ UserName - Returns the value of the User name EA
+
+ Password - Returns the value of the password EA
+
+ ShareType - Returns the value of the share type EA
+
+ CredentialExtension - Returns whether or not this create
+ should use the provided credentials for an credential
+ extended connection. This is primarily for OleDs
+ accessing the ds in multiple security contexts.
+
+Return Value:
+
+ NTSTATUS - Status of operation
+
+--*/
+{
+
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( UserName, NULL );
+ RtlInitUnicodeString( Password, NULL );
+ *ShareType = RESOURCETYPE_ANY;
+ if ( CredentialExtension ) {
+ *CredentialExtension = FALSE;
+ }
+
+ DebugTrace(+1, Dbg, "ReadAttachEas....\n", 0);
+
+ if ( EaBuffer != NULL) {
+
+ while (TRUE) {
+ ULONG EaNameLength = EaBuffer->EaNameLength;
+
+ if (strcmp(EaBuffer->EaName, EA_NAME_USERNAME) == 0) {
+
+ UserName->Length = EaBuffer->EaValueLength;
+ UserName->MaximumLength = EaBuffer->EaValueLength;
+ UserName->Buffer = (PWSTR)(EaBuffer->EaName+EaNameLength+1);
+
+ } else if (strcmp(EaBuffer->EaName, EA_NAME_PASSWORD) == 0) {
+
+ Password->Length = EaBuffer->EaValueLength;
+ Password->MaximumLength = EaBuffer->EaValueLength;
+ Password->Buffer = (PWSTR)(EaBuffer->EaName+EaNameLength+1);
+
+ } else if (strcmp(EaBuffer->EaName, EA_NAME_TYPE) == 0) {
+
+ *ShareType = *(ULONG UNALIGNED *)(EaBuffer->EaName+EaNameLength+1);
+
+ } else if (strcmp(EaBuffer->EaName, EA_NAME_CREDENTIAL_EX) == 0) {
+
+ if ( CredentialExtension ) {
+ *CredentialExtension = TRUE;
+ DebugTrace(0, Dbg, "ReadAttachEas signals a credential extension.\n", 0 );
+ }
+
+ } else {
+ DebugTrace(0, Dbg, "ReadAttachEas Unknown EA -> %s\n", EaBuffer->EaName);
+ }
+
+ if (EaBuffer->NextEntryOffset == 0) {
+ break;
+ } else {
+ EaBuffer = (PFILE_FULL_EA_INFORMATION) ((PCHAR) EaBuffer+EaBuffer->NextEntryOffset);
+ }
+ }
+ }
+
+ DebugTrace(-1, Dbg, "ReadAttachEas -> %08lx\n", STATUS_SUCCESS);
+
+ return STATUS_SUCCESS;
+
+}
+
+
+IO_STATUS_BLOCK
+OpenRedirector(
+ IN PIRP_CONTEXT IrpContext,
+ ULONG DesiredAccess,
+ ULONG ShareAccess,
+ PFILE_OBJECT FileObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routines opens a handle to the redirector device.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ DesiredAccess - The requested access to the redirector.
+
+ ShareAccess - The requested share access to the redirector.
+
+ FileObject - A pointer to the caller file object.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+
+{
+ IO_STATUS_BLOCK iosb;
+
+ PAGED_CODE();
+
+ //
+ // Note that the object manager will only allow an administrator
+ // to open the redir itself. This is good.
+ //
+
+ DebugTrace(+1, Dbg, "NwOpenRedirector\n", 0);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ //
+ // Set the new share access
+ //
+
+ if (!NT_SUCCESS(iosb.Status = IoCheckShareAccess( DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &NwRcb.ShareAccess,
+ TRUE ))) {
+
+ DebugTrace(0, Dbg, "bad share access\n", 0);
+
+ try_return( NOTHING );
+ }
+
+ NwSetFileObject( FileObject, NULL, &NwRcb );
+ ++NwRcb.OpenCount;
+
+ //
+ // Set the return status.
+ //
+
+ iosb.Status = STATUS_SUCCESS;
+ iosb.Information = FILE_OPENED;
+
+ try_exit: NOTHING;
+ } finally {
+
+ NwReleaseRcb( &NwRcb );
+ DebugTrace(-1, Dbg, "NwOpenRedirector -> Iosb.Status = %08lx\n", iosb.Status);
+
+ }
+
+ //
+ // Return to the caller.
+ //
+
+ return iosb;
+}
+
+
+IO_STATUS_BLOCK
+CreateRemoteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routines opens a remote file or directory.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ DriveName - The drive name. One of three forms X:, LPTx, or NULL.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ ULONG DesiredAccess;
+ ULONG ShareAccess;
+ PFILE_OBJECT FileObject;
+
+ UNICODE_STRING FileName;
+ PFILE_OBJECT RelatedFileObject;
+ ULONG Options;
+ ULONG FileAttributes;
+
+ BOOLEAN CreateDirectory;
+ BOOLEAN OpenDirectory;
+ BOOLEAN DirectoryFile;
+ BOOLEAN NonDirectoryFile;
+ BOOLEAN DeleteOnClose;
+ BOOLEAN OpenTargetDirectory;
+ ULONG AllocationSize;
+
+ // Unhandled open features.
+
+ // PFILE_FULL_EA_INFORMATION EaBuffer;
+ // ULONG EaLength;
+ // BOOLEAN SequentialOnly;
+ // BOOLEAN NoIntermediateBuffering;
+ // BOOLEAN IsPagingFile;
+ // BOOLEAN NoEaKnowledge;
+
+ ULONG CreateDisposition;
+
+ PFCB Fcb = NULL;
+ PICB Icb = NULL;
+ PDCB Dcb;
+ PVCB Vcb = NULL;
+ PSCB Scb;
+
+ BOOLEAN IsAFile;
+ BOOLEAN MayBeADirectory = FALSE;
+ BOOLEAN OwnOpenLock = FALSE;
+ BOOLEAN SetShareAccess = FALSE;
+
+ BYTE SearchFlags;
+ BYTE ShareFlags;
+
+ BOOLEAN CreateTreeConnection;
+ PUNICODE_STRING VolumeName;
+
+ NTSTATUS Status;
+ UNICODE_STRING NdsConnectName;
+ WCHAR ConnectBuffer[MAX_NDS_NAME_CHARS];
+ BOOLEAN MadeUidNdsName = FALSE;
+
+ PAGED_CODE();
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+ ShareAccess = IrpSp->Parameters.Create.ShareAccess;
+ FileObject = IrpSp->FileObject;
+ OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY );
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+
+ try {
+
+ //
+ // Reference our input parameters to make things easier
+ //
+
+ RelatedFileObject = FileObject->RelatedFileObject;
+
+ //
+ // We actually want the parsed file name.
+ // FileName = FileObject->FileName;
+ //
+ FileName = IrpContext->Specific.Create.FullPathName;
+ Options = IrpSp->Parameters.Create.Options;
+ FileAttributes = IrpSp->Parameters.Create.FileAttributes;
+ AllocationSize = Irp->Overlay.AllocationSize.LowPart;
+
+ //
+ // Short circuit an attempt to open a wildcard name.
+ //
+
+ if ( FsRtlDoesNameContainWildCards( &FileName ) ) {
+ try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
+ }
+
+ // Decipher Option flags and values
+ //
+
+ DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE );
+ NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE );
+ DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
+
+ //
+ // Things we currently ignore, because netware servers don't support it.
+ //
+
+ // SequentialOnly = BooleanFlagOn( Options, FILE_SEQUENTIAL_ONLY );
+ // NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING );
+ // NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE );
+ // EaBuffer = Irp->AssociatedIrp.SystemBuffer;
+ // EaLength = IrpSp->Parameters.Create.EaLength;
+ // IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE );
+
+ if ( BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION ) ) {
+ CreateDisposition = FILE_OPEN;
+ } else {
+ CreateDisposition = (Options >> 24) & 0x000000ff;
+ }
+
+ CreateDirectory = (BOOLEAN)(DirectoryFile &&
+ ((CreateDisposition == FILE_CREATE) ||
+ (CreateDisposition == FILE_OPEN_IF)));
+
+ OpenDirectory = (BOOLEAN)(DirectoryFile &&
+ ((CreateDisposition == FILE_OPEN) ||
+ (CreateDisposition == FILE_OPEN_IF)));
+
+ Dcb = NULL;
+ if ( RelatedFileObject != NULL ) {
+
+ PNONPAGED_DCB NonPagedDcb;
+
+ NonPagedDcb = RelatedFileObject->FsContext;
+ Dcb = NonPagedDcb->Fcb;
+
+ //
+ // If there is a related file object then this is a relative open
+ // and it better be a DCB.
+ //
+
+ if ( NodeType( Dcb ) != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Bad file name\n", 0);
+ Iosb.Status = STATUS_OBJECT_NAME_INVALID;
+ try_return( Iosb );
+ }
+
+
+ //
+ // Obtain SCB pointers.
+ //
+
+ IrpContext->pScb = Dcb->Scb;
+ IrpContext->pNpScb = Dcb->Scb->pNpScb;
+ }
+
+ //
+ // We are about ready to send a packet. Append this IRP context
+ // the SCB workqueue, and wait until it gets to the front.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ //
+ // Acquire the Global FCB resource to ensure that one thread
+ // can't access the half created FCB of another thread.
+ //
+
+ NwAcquireOpenLock( );
+ OwnOpenLock = TRUE;
+
+ //
+ // Find the volume for this file.
+ //
+
+ CreateTreeConnection = BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION );
+
+ if ( CreateTreeConnection ) {
+ VolumeName = &IrpContext->Specific.Create.FullPathName;
+ } else {
+ VolumeName = &IrpContext->Specific.Create.VolumeName;
+ }
+
+ if ( Dcb == NULL ) {
+
+RetryFindVcb:
+
+ Vcb = NwFindVcb(
+ IrpContext,
+ VolumeName,
+ IrpContext->Specific.Create.ShareType,
+ IrpContext->Specific.Create.DriveLetter,
+ CreateTreeConnection,
+ ( BOOLEAN )( CreateTreeConnection && DeleteOnClose ) );
+
+ if ( Vcb == NULL ) {
+
+ //
+ // If this create failed because we need nds data, get
+ // the data from the ds and resubmit the request.
+ //
+
+ if ( IrpContext->Specific.Create.NdsCreate &&
+ IrpContext->Specific.Create.NeedNdsData ) {
+
+ //
+ // Release the open resource so we can move around.
+ //
+
+ NwReleaseOpenLock( );
+ OwnOpenLock = FALSE;
+
+ //
+ // Take the volume name and build the server/share
+ // connect name.
+ //
+
+ NdsConnectName.Buffer = ConnectBuffer;
+ NdsConnectName.MaximumLength = sizeof( ConnectBuffer );
+ NdsConnectName.Length = 0;
+
+ //
+ // Get the ds information. We may jump servers here.
+ //
+
+ Status = NdsMapObjectToServerShare( IrpContext,
+ &Scb,
+ &NdsConnectName,
+ CreateTreeConnection,
+ &(IrpContext->Specific.Create.dwNdsOid) );
+
+ if( !NT_SUCCESS( Status ) ) {
+ ExRaiseStatus( Status );
+ }
+
+ //
+ // Make sure we are on the scb queue after all the
+ // possible server jumping.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireOpenLock( );
+ OwnOpenLock = TRUE;
+
+ //
+ // Prepend the Uid to the server/share name.
+ //
+
+ MergeStrings( &IrpContext->Specific.Create.UidConnectName,
+ &Scb->UnicodeUid,
+ &NdsConnectName,
+ PagedPool );
+
+ MadeUidNdsName = TRUE;
+
+ //
+ // We have the data, so re-do the connect.
+ //
+
+ IrpContext->Specific.Create.NeedNdsData = FALSE;
+ goto RetryFindVcb;
+
+ } else {
+
+ //
+ // If this was an open to delete a tree connect, and we failed
+ // to find the VCB, simply return the error.
+ //
+
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ try_return ( Iosb );
+
+ }
+
+ }
+
+ } else {
+
+ Vcb = Dcb->Vcb;
+ NwReferenceVcb( Vcb );
+
+ }
+
+ ASSERT( Vcb->Scb == IrpContext->pScb );
+
+ //
+ // If this is the target name for a rename then we want to find the
+ // DCB for the parent directory.
+ //
+
+ if (OpenTargetDirectory) {
+
+ Iosb = OpenRenameTarget(IrpContext, Vcb, Dcb, &Icb );
+ if (Icb != NULL) {
+ Fcb = Icb->SuperType.Fcb;
+ }
+ try_return ( Iosb );
+
+ }
+
+ //
+ // Find the FCB for this file. If the FCB exists, we get a
+ // referenced pointer. Otherwise a new FCB is created.
+ //
+
+ Fcb = NwFindFcb( IrpContext->pScb, Vcb, &FileName, Dcb );
+
+ //
+ // Check the share access for this file. The share access
+ // is updated if access is granted.
+ //
+
+ if ( Fcb->IcbCount > 0 ) {
+
+ Iosb.Status = IoCheckShareAccess(
+ DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &Fcb->ShareAccess,
+ TRUE );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ try_return( Iosb );
+ }
+
+ } else {
+
+ IoSetShareAccess(
+ DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &Fcb->ShareAccess );
+ }
+
+ SetShareAccess = TRUE;
+
+ //
+ // Now create the ICB.
+ //
+
+ Icb = NwCreateIcb( NW_NTC_ICB, Fcb );
+ Icb->FileObject = FileObject;
+ NwSetFileObject( FileObject, Fcb->NonPagedFcb, Icb );
+
+#ifndef QFE_BUILD
+
+ //
+ // Supply a resource for the modified page write to grab when
+ // writing mem mapped files. We do this because it is imposed
+ // on us by the system, we do not require the resource for any
+ // real serialization.
+ //
+
+ Fcb->NonPagedFcb->Header.Flags = 0;
+ Fcb->NonPagedFcb->Header.Resource = NULL;
+
+#endif
+
+#ifdef NWFASTIO
+ //
+ // Initialize private cache map so that the i/o system will call
+ // our fast path.
+ //
+
+ FileObject->PrivateCacheMap = (PVOID)1;
+#endif
+
+ IrpContext->Icb = Icb;
+
+ //
+ // Allocate an 8 bit PID for this ICB. Use different thread so
+ // each Wow program gets its own id. This is because if the same id
+ // has locks using two handles and closes just one of them the locks
+ // on that handle are not discarded.
+ //
+
+ Iosb.Status = NwMapPid( (ULONG)PsGetCurrentThread(), &Icb->Pid );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ try_return( Iosb.Status );
+ }
+
+ //
+ // Try to figure out what it is we're expected to open.
+ //
+
+ Iosb.Status = STATUS_SUCCESS;
+
+ if ( FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ //
+ // Opening a print queue job.
+ //
+
+ Iosb = CreatePrintJob( IrpContext, Vcb, Icb, DriveName );
+
+ } else if ( DirectoryFile ||
+ ( Fcb->State == FCB_STATE_OPENED &&
+ Fcb->NodeTypeCode == NW_NTC_DCB ) ) {
+
+ //
+ // Opening a directory.
+ //
+
+ MayBeADirectory = TRUE;
+
+ switch ( CreateDisposition ) {
+
+ case FILE_OPEN:
+ Iosb = ChangeDirectory( IrpContext, Vcb, Icb );
+ break;
+
+ case FILE_CREATE:
+ Iosb = CreateDir( IrpContext, Vcb, Icb );
+ break;
+
+ case FILE_OPEN_IF:
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ //
+ // If the opener specified a directory, fail this request
+ // if the object is a file.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) && IsAFile ) {
+ Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ } else if ( !NT_SUCCESS( Iosb.Status )) {
+ Iosb = CreateDir( IrpContext, Vcb, Icb );
+ }
+ break;
+
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE:
+ case FILE_OVERWRITE_IF:
+ Iosb.Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ default:
+ KeBugCheck( RDR_FILE_SYSTEM );
+
+ }
+
+ } else {
+
+ SearchFlags = NtAttributesToNwAttributes( FileAttributes );
+ ShareFlags = NtToNwShareFlags( DesiredAccess, ShareAccess );
+
+ IsAFile = NonDirectoryFile ||
+ (Fcb->State == FCB_STATE_OPENED &&
+ Fcb->NodeTypeCode == NW_NTC_FCB );
+ //
+ // Assume we are opening a file. If that fails, and it makes
+ // sense try to open a directory.
+ //
+
+ switch ( CreateDisposition ) {
+
+ case FILE_OPEN:
+
+ //
+ // If the disposition is FILE_OPEN try to avoid an unneeded
+ // open, for some desired access types.
+ //
+
+ switch ( DesiredAccess & ~SYNCHRONIZE ) {
+
+ case FILE_WRITE_ATTRIBUTES:
+ case FILE_READ_ATTRIBUTES:
+ case DELETE:
+
+ Iosb.Status = FileOrDirectoryExists(
+ IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ if ( !IsAFile) {
+ MayBeADirectory = TRUE;
+ }
+
+ //
+ // Fail open of read only file for delete access,
+ // since the netware server won't fail the delete.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) &&
+ CreateDisposition == DELETE &&
+ FlagOn( Icb->NpFcb->Attributes, NW_ATTRIBUTE_READ_ONLY ) ) {
+
+ Iosb.Status = STATUS_ACCESS_DENIED;
+ }
+
+ if ( ( Iosb.Status == STATUS_OBJECT_NAME_NOT_FOUND ) &&
+ ( (DesiredAccess & ~SYNCHRONIZE) == DELETE ) ) {
+ //
+ // we may not have scan rights. fake the return as OK.
+ // NW allows the delete without scan rights.
+ //
+ Iosb.Status = STATUS_SUCCESS;
+ }
+
+ break;
+
+ default:
+
+ Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+
+ if ( ( Iosb.Status == STATUS_OBJECT_NAME_NOT_FOUND ||
+ Iosb.Status == STATUS_FILE_IS_A_DIRECTORY )
+ && !IsAFile) {
+
+ //
+ // Opener didn't specify file or directory, and open
+ // file failed. So try open directory.
+ //
+
+ Iosb = ChangeDirectory( IrpContext, Vcb, Icb );
+ MayBeADirectory = TRUE;
+
+ } else if ( (Iosb.Status == STATUS_SHARING_VIOLATION) &&
+ ((ShareFlags == (NW_OPEN_FOR_READ | NW_DENY_WRITE)) ||
+ (ShareFlags == (NW_OPEN_FOR_READ)))) {
+
+ //
+ // if the file was already open exclusive (eg. GENERIC_EXECUTE)
+ // then a debugger opening it again for read will fail with
+ // sharing violation. In this case, we will try open exclusive
+ // again to see if that passes.
+ //
+
+ ShareFlags |= NW_OPEN_EXCLUSIVE ;
+ ShareFlags &= ~(NW_DENY_WRITE | NW_DENY_READ);
+ Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ }
+
+ break;
+
+ }
+
+ break;
+
+ case FILE_CREATE:
+ Iosb = CreateNewFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ break;
+
+ case FILE_OPEN_IF:
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ } else {
+ Iosb = CreateNewFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ }
+
+ if ( !NT_SUCCESS( Iosb.Status ) && !IsAFile) {
+
+ //
+ // Opener didn't specify file or directory, and open
+ // file and create new file both failed. So try open
+ // or create directory.
+ //
+
+ MayBeADirectory = TRUE;
+ Iosb.Status = FileOrDirectoryExists(
+ IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile);
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Information = FILE_OPENED;
+ } else {
+ Iosb = CreateDir( IrpContext, Vcb, Icb );
+ }
+ }
+
+ break;
+
+ //
+ // None of the below make sense for directories so if the
+ // file operation fails, just return the failure status
+ // to the user.
+ //
+
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE_IF:
+
+ //
+ // Actually, if Overwrite is chosen, we are supposed to
+ // get the attributes for a file and OR them with the
+ // new attributes.
+ //
+
+ Iosb = CreateOrOverwriteFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags, FALSE );
+ break;
+
+ case FILE_OVERWRITE:
+ Iosb.Status = FileOrDirectoryExists(
+ IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb = CreateOrOverwriteFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags, FALSE );
+ }
+
+ break;
+
+ default:
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ if ( Vcb != NULL ) {
+ NwDereferenceVcb( Vcb, IrpContext, FALSE );
+ }
+
+ if ( MadeUidNdsName ) {
+ FREE_POOL( IrpContext->Specific.Create.UidConnectName.Buffer );
+ }
+
+ if ( AbnormalTermination() || !NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // Remove the share access if necessary
+ //
+
+ if ( SetShareAccess ) {
+ IoRemoveShareAccess( FileObject, &Fcb->ShareAccess );
+ }
+
+ //
+ // Failed to create
+ //
+
+ if ( Icb != NULL ) {
+
+ if ( Icb->Pid != 0 ) {
+ NwUnmapPid( Icb->Pid, NULL );
+ }
+
+ NwDeleteIcb( NULL, Icb );
+ }
+
+ //
+ // If this was a tree connect, derefence the extra
+ // reference on the VCB.
+ //
+
+ if ( CreateTreeConnection && !DeleteOnClose ) {
+ if ( Vcb != NULL ) {
+ NwDereferenceVcb( Vcb, IrpContext, FALSE );
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ } else {
+
+ Icb->State = ICB_STATE_OPENED;
+ if ( Fcb->State == FCB_STATE_OPEN_PENDING ) {
+ Fcb->State = FCB_STATE_OPENED;
+ }
+
+ if ( DeleteOnClose && !CreateTreeConnection ) {
+ SetFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+ }
+
+ FileObject->SectionObjectPointer = &Fcb->NonPagedFcb->SegmentObject;
+
+ if ( MayBeADirectory ) {
+
+ //
+ // We successfully opened the file as a directory.
+ // If the DCB is newly created, it will be marked
+ // type FCB, update it.
+ //
+
+ Fcb->NodeTypeCode = NW_NTC_DCB;
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ }
+
+ if ( OwnOpenLock ) {
+ NwReleaseOpenLock( );
+ }
+
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+ChangeDirectory(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ )
+/*++
+
+Routine Description:
+
+ This routines sets the directory for a remote drive.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the file we are opening.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ BYTE Attributes;
+ BOOLEAN FirstTime = TRUE;
+
+ PAGED_CODE();
+
+ //
+ // No need to send a packet if we are opening the root of the volume.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) {
+
+ Iosb.Status = STATUS_SUCCESS;
+ Iosb.Information = FILE_OPENED;
+
+ return( Iosb );
+ }
+
+Retry:
+
+ if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_DIRECTORIES,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+ }
+
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Vcb->Specific.Disk.LongNameSpace,
+ Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_MODIFY_TIME,
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_b",
+ 4,
+ &Attributes );
+ }
+
+ //
+ // Unfortunately, this succeeds even if the file in question
+ // is not a directory.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) &&
+ ( !FlagOn( Attributes, NW_ATTRIBUTE_DIRECTORY ) ) ) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+
+ if ((Iosb.Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Vcb );
+
+ goto Retry;
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+
+ Fcb->NonPagedFcb->Attributes = (UCHAR)Attributes;
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_OPENED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreateDir(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ )
+/*++
+
+Routine Description:
+
+ This routines create a new directory.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+
+ PAGED_CODE();
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) {
+ Iosb.Status = STATUS_ACCESS_DENIED;
+ return( Iosb );
+ }
+
+ if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ return( Iosb );
+
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_CREATE_DIRECTORY,
+ Vcb->Specific.Disk.Handle,
+ 0xFF,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_CREATE,
+ 0, // Search Flags,
+ 0, // Return Info Mask
+ NW_ATTRIBUTE_DIRECTORY,
+ 0x00ff, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_CREATED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ return( Iosb );
+}
+
+
+NTSTATUS
+FileOrDirectoryExists(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb OPTIONAL,
+ PUNICODE_STRING Name,
+ OUT PBOOLEAN IsAFile
+ )
+/*++
+
+Routine Description:
+
+ This routines looks to see if a file or directory exists.
+
+Arguments:
+
+ IrpContext - Supplies allx the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB for the file we are looking for.
+
+ Name - Fully qualified name.
+
+ IsAFile - Returns TRUE is the found file is a file, FALSE if it is
+ a directory. Return nothing if the function returns FALSE.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ ULONG Attributes;
+ ULONG FileSize;
+ USHORT LastModifiedDate;
+ USHORT LastModifiedTime;
+ USHORT CreationDate;
+ USHORT CreationTime = DEFAULT_TIME;
+ USHORT LastAccessDate;
+ NTSTATUS Status;
+ PFCB Fcb;
+ BOOLEAN FirstTime = TRUE;
+
+ PAGED_CODE();
+
+ //
+ // No need to send a packet if we are searching for the root of the volume.
+ //
+
+ if ( Name->Length == 0 ) {
+ *IsAFile = FALSE;
+
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Decide how to handle this request. If we have an ICB, use the FCB
+ // to determine the file name type, otherwise we have to make the
+ // decision here.
+ //
+
+ if ( Icb != NULL &&
+ !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ||
+
+ Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ||
+
+ IsFatNameValid( Name ) ) {
+Retry:
+ //
+ // First try a file
+ //
+
+ IrpContext->ResponseLength = 0;
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ Name );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b-dwwww",
+ 14,
+ &Attributes,
+ &FileSize,
+ &CreationDate,
+ &LastAccessDate,
+ &LastModifiedDate,
+ &LastModifiedTime );
+ }
+
+ if ((Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Vcb );
+
+ goto Retry;
+ }
+
+ if ( Status == STATUS_UNSUCCESSFUL ) {
+
+ //
+ // Not a file, Is it a directory?
+ //
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_DIRECTORIES,
+ Name );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+ }
+
+ //
+ // If the exchange or ParseResponse fails then exit with not found
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( STATUS_OBJECT_NAME_NOT_FOUND );
+ }
+
+ *IsAFile = FALSE;
+ ASSERT( (Attributes & NW_ATTRIBUTE_DIRECTORY) != 0 );
+
+ } else {
+
+ if ( Status == STATUS_UNEXPECTED_NETWORK_ERROR &&
+ IrpContext->ResponseLength >= sizeof( NCP_RESPONSE ) ) {
+
+ //
+ // Work-around for netware bug. If netware returns short
+ // packet, just return success. We exit prematurely
+ // because we have no attributes to record.
+ //
+
+ Icb = NULL;
+ *IsAFile = TRUE;
+ return ( STATUS_SUCCESS );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ *IsAFile = TRUE;
+ ASSERT( ( Attributes & NW_ATTRIBUTE_DIRECTORY ) == 0 );
+
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Vcb->Specific.Disk.LongNameSpace,
+ Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0,
+ Name );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e=e_xx_xx_x",
+ 4,
+ &Attributes,
+ &FileSize,
+ 6,
+ &CreationTime,
+ &CreationDate,
+ 4,
+ &LastModifiedTime,
+ &LastModifiedDate,
+ 4,
+ &LastAccessDate );
+ }
+
+ //
+ // If the exchange or ParseResponse fails then exit with not found
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( STATUS_OBJECT_NAME_NOT_FOUND );
+ }
+
+ if ( Attributes & NW_ATTRIBUTE_DIRECTORY) {
+ *IsAFile = FALSE;
+ } else {
+ *IsAFile = TRUE;
+ }
+ }
+
+ //
+ // If the caller supplied an ICB, update the FCB attributes.
+ // We'll use this info if the caller does a query attributes
+ // on the ICB.
+ //
+
+ if ( Icb != NULL && *IsAFile ) {
+
+ Fcb = Icb->SuperType.Fcb;
+ ASSERT( Fcb->NodeTypeCode == NW_NTC_FCB );
+
+ Fcb->NonPagedFcb->Attributes = (UCHAR)Attributes;
+ Fcb->NonPagedFcb->Header.FileSize.QuadPart = FileSize;
+ Fcb->LastModifiedDate = LastModifiedDate;
+ Fcb->LastModifiedTime = LastModifiedTime;
+ Fcb->CreationTime = CreationTime;
+ Fcb->CreationDate = CreationDate;
+ Fcb->LastAccessDate = LastAccessDate;
+
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate );
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+IO_STATUS_BLOCK
+OpenFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE Attributes,
+ IN BYTE OpenFlags
+ )
+/*++
+
+Routine Description:
+
+ This routines sets opens a file on a netware server. It fails if
+ the file does not exist.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB we are opening.
+
+ Attributes - Open attributes.
+
+ OpenFlags - Open mode and sharing mode flags.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+
+ PAGED_CODE();
+
+ //
+ // No need to send a packet if we are trying to open the root of
+ // the volume as a file.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) {
+ Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
+ return( Iosb );
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+ ASSERT( NodeType( Fcb ) == NW_NTC_FCB );
+
+ //
+ // Send the open request and wait for the response.
+ //
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbJ",
+ NCP_OPEN_FILE,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ OpenFlags,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( ( ReadExecOnlyFiles ) &&
+ ( !NT_SUCCESS( Iosb.Status ) ) ) {
+
+ //
+ // Retry the open with the appropriate flags for
+ // execute only files.
+ //
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbJ",
+ NCP_OPEN_FILE,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_EXEC_ONLY_FILES,
+ OpenFlags,
+ &Icb->SuperType.Fcb->RelativeFileName );
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr=_b-dwwww",
+ Icb->Handle,
+ sizeof( Icb->Handle ),
+ 14,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ &Fcb->CreationDate,
+ &Fcb->LastAccessDate,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ Fcb->CreationTime = DEFAULT_TIME;
+
+ }
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_OPEN,
+ NW_ATTRIBUTE_HIDDEN | NW_ATTRIBUTE_SYSTEM, // Search Flags,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ 0, // Create attributes
+ OpenFlags, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( ( ReadExecOnlyFiles ) &&
+ ( !NT_SUCCESS( Iosb.Status ) ) ) {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_OPEN,
+ NW_ATTRIBUTE_EXEC_ONLY,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ 0, // Create attributes
+ OpenFlags, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Ne_e=e_xx_xx_x",
+ &Icb->Handle[2],
+ 6,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ 6,
+ &Fcb->CreationTime,
+ &Fcb->CreationDate,
+ 4,
+ &Fcb->LastModifiedTime,
+ &Fcb->LastModifiedDate,
+ 4,
+ &Fcb->LastAccessDate );
+ }
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // NT does not allow you to open a read only file for write access.
+ // Netware does. To fake NT semantics, check to see if we should
+ // fail the open that the netware server just succeeded.
+ //
+
+ if ( ( Fcb->NonPagedFcb->Attributes & NW_ATTRIBUTE_READ_ONLY ) &&
+ ( OpenFlags & NW_OPEN_FOR_WRITE ) ) {
+
+ CloseFile( IrpContext, Icb );
+ Iosb.Status = STATUS_ACCESS_DENIED;
+ }
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ Icb->HasRemoteHandle = TRUE;
+
+
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate );
+
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_OPENED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreateNewFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE CreateAttributes,
+ IN BYTE OpenFlags
+ )
+/*++
+
+Routine Description:
+
+ This routines creates a new file on a netware server. It fails
+ if the file exists.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB we are opening.
+
+ CreateAttributes - Create attributes.
+
+ OpenFlags - Open mode and sharing mode flags.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ UCHAR DelayedAttributes;
+ BOOLEAN CloseAndReopen;
+
+ PAGED_CODE();
+
+ //
+ // If the user opens the file for shared access, then we will need to
+ // create the file close, then reopen it (since we have no NCP to say
+ // create with shared access). If the file is being created read-only,
+ // and the creator requests write access then we pull the additional
+ // trick of creating the file without the read-only, and set it later,
+ // so that the second open can succeed.
+ //
+
+ CloseAndReopen = FALSE;
+ DelayedAttributes = 0;
+
+ if ( OpenFlags != NW_OPEN_EXCLUSIVE ) {
+ CloseAndReopen = TRUE;
+
+ if ( ( CreateAttributes & NW_ATTRIBUTE_READ_ONLY ) &&
+ ( OpenFlags & NW_OPEN_FOR_WRITE ) ) {
+
+ DelayedAttributes = CreateAttributes;
+ CreateAttributes = 0;
+ }
+ }
+
+ //
+ // Send the create request and wait for the response.
+ //
+
+ Fcb = Icb->SuperType.Fcb;
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ return( Iosb );
+
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbJ", // NCP Create New File
+ NCP_CREATE_NEW_FILE,
+ Vcb->Specific.Disk.Handle,
+ CreateAttributes,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr=_b-dwwww",
+ Icb->Handle, sizeof( Icb->Handle ),
+ 14,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ &Fcb->CreationDate,
+ &Fcb->LastAccessDate,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ Fcb->CreationTime = DEFAULT_TIME;
+
+ }
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_CREATE,
+ 0, // Search Flags
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ CreateAttributes,
+ 0, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Ne_e=e_xx_xx_x",
+ &Icb->Handle[2],
+ 6,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ 6,
+ &Fcb->CreationTime,
+ &Fcb->CreationDate,
+ 4,
+ &Fcb->LastModifiedTime,
+ &Fcb->LastModifiedDate,
+ 4,
+ &Fcb->LastAccessDate );
+ }
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ Icb->HasRemoteHandle = TRUE;
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "LastAcceDate-> %08lx\n", Fcb->LastAccessDate );
+ }
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ return( Iosb );
+ }
+
+
+ //
+ // We've created the file, and the users wants shared access to the
+ // file. Close the file and reopen in sharing mode.
+ //
+
+ if ( CloseAndReopen ) {
+ CloseFile( IrpContext, Icb );
+ Iosb = OpenFile( IrpContext, Vcb, Icb, CreateAttributes, OpenFlags );
+ }
+
+ //
+ // If we need to set attributes, do it now. Ignore errors, if any.
+ //
+
+ if ( DelayedAttributes != 0 ) {
+
+ ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ DelayedAttributes,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_CREATED;
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreateOrOverwriteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE CreateAttributes,
+ IN BYTE OpenFlags,
+ IN BOOLEAN CreateOperation
+ )
+/*++
+
+Routine Description:
+
+ This routines creates a file on a netware server. If the file
+ exists it is overwritten.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB we are opening.
+
+ Attributes - Open attributes.
+
+ OpenFlags - Open mode and sharing mode flags.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ UCHAR DelayedAttributes;
+ BOOLEAN CloseAndReopen;
+
+ PAGED_CODE();
+
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // Send the request and wait for the response.
+ //
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ return( Iosb );
+
+ }
+
+ //
+ // If the user opens the file for shared access, then we will need to
+ // create the file close, then reopen it (since we have no NCP to say
+ // create with shared access). If the file is being created read-only,
+ // and the creator requests write access then we pull the additional
+ // trick of creating the file without the read-only, and set it later,
+ // so that the second open can succeed.
+ //
+
+ if ( ( CreateAttributes & NW_ATTRIBUTE_READ_ONLY ) &&
+ ( OpenFlags & NW_OPEN_FOR_WRITE ) ) {
+
+ DelayedAttributes = CreateAttributes;
+ CreateAttributes = 0;
+ } else {
+ DelayedAttributes = 0;
+ }
+
+ //
+ // Dos namespace create always returns the file exclusive.
+ //
+
+ if (!FlagOn(OpenFlags, NW_OPEN_EXCLUSIVE)) {
+ CloseAndReopen = TRUE;
+ } else {
+ CloseAndReopen = FALSE;
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbJ",
+ NCP_CREATE_FILE,
+ Vcb->Specific.Disk.Handle,
+ CreateAttributes,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr=_b-dwwww",
+ Icb->Handle,
+ sizeof( Icb->Handle ),
+ 14,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ &Fcb->CreationDate,
+ &Fcb->LastAccessDate,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ Fcb->CreationTime = DEFAULT_TIME;
+
+ }
+
+ //
+ // We've created the file, and the users wants shared access to the
+ // file. Close the file and reopen in sharing mode.
+ //
+
+ if (( NT_SUCCESS( Iosb.Status ) ) &&
+ ( CloseAndReopen )) {
+
+ CloseFile( IrpContext, Icb );
+ Iosb = OpenFile( IrpContext, Vcb, Icb, CreateAttributes, OpenFlags );
+ }
+
+ if ( DelayedAttributes != 0 ) {
+ ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ DelayedAttributes,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+ }
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_OVERWRITE,
+ 0, // Search Flags
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ CreateAttributes,
+ OpenFlags, // DesiredAccess
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Ne_e=e_xx_xx_x",
+ &Icb->Handle[2],
+ 6,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ 6,
+ &Fcb->CreationTime,
+ &Fcb->CreationDate,
+ 4,
+ &Fcb->LastModifiedTime,
+ &Fcb->LastModifiedDate,
+ 4,
+ &Fcb->LastAccessDate );
+ }
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ Icb->HasRemoteHandle = TRUE;
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate );
+ } else {
+ return( Iosb );
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ if ( CreateOperation) {
+ Iosb.Information = FILE_CREATED;
+ } else {
+ Iosb.Information = FILE_OVERWRITTEN;
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+OpenRenameTarget(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PDCB Dcb,
+ IN PICB* Icb
+ )
+/*++
+
+Routine Description:
+
+ This routine opens a directory. If the filename provided specifies
+ a directory then the file/directory to be renamed will be put in this
+ directory.
+
+ If the target foo\bar does not exist or is a file then the source of
+ the rename must be a file and will end up in the directory foo with
+ the name bar
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Dcb - A pointer to the DCB for relative opens. If NULL the FileName
+ is an full path name. If non NUL the FileName is relative to
+ this directory.
+
+ Icb - A pointer to where the address of the Icb is to be stored.
+
+Return Value:
+
+ NT_STATUS - Status of operation
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ BOOLEAN FullNameIsAFile;
+ BOOLEAN FullNameExists;
+ BOOLEAN PathIsAFile;
+
+#if 0
+ UNICODE_STRING Drive;
+ WCHAR DriveLetter;
+ UNICODE_STRING Server;
+ UNICODE_STRING Volume;
+ UNICODE_STRING FileName;
+#endif
+ UNICODE_STRING Path;
+ UNICODE_STRING FullName;
+ UNICODE_STRING CompleteName;
+ UNICODE_STRING VcbName;
+ PWCH pTrailingSlash;
+
+ USHORT i;
+ USHORT DcbNameLength;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "OpenRenameTarget\n", 0);
+
+ //
+ // Get the current IRP stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Build a complete filename of the form \g:\server\volume\dir1\file
+ //
+
+ if ( Dcb != NULL ) {
+
+ //
+ // Strip to UID portion of the DCB name.
+ //
+
+ for ( i = 0 ; i < Dcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
+ if ( Dcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
+ break;
+ }
+ }
+
+ ASSERT( Dcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
+
+ //
+ // Now build the full name by appending the file name to the DCB name.
+ //
+
+ DcbNameLength = Dcb->FullFileName.Length - ( i * sizeof(WCHAR) );
+ CompleteName.Length = DcbNameLength + IrpSp->FileObject->FileName.Length + sizeof( WCHAR);
+ CompleteName.MaximumLength = CompleteName.Length;
+ CompleteName.Buffer = ALLOCATE_POOL_EX( PagedPool, CompleteName.Length );
+
+ RtlCopyMemory(
+ CompleteName.Buffer,
+ Dcb->FullFileName.Buffer + i,
+ DcbNameLength );
+
+ CompleteName.Buffer[ DcbNameLength / sizeof(WCHAR) ] = L'\\';
+
+ RtlCopyMemory(
+ CompleteName.Buffer + DcbNameLength / sizeof(WCHAR ) + 1,
+ IrpSp->FileObject->FileName.Buffer,
+ IrpSp->FileObject->FileName.Length );
+
+ Dcb = NULL;
+
+ } else {
+
+ CompleteName = IrpSp->FileObject->FileName;
+
+ }
+
+ //
+ // Calculate the VCB name, without the UID prefix.
+ //
+
+ VcbName.Buffer = wcschr( Vcb->Name.Buffer, L'\\' );
+ VcbName.Length = Vcb->Name.Length -
+ ( (PCHAR)VcbName.Buffer - (PCHAR)Vcb->Name.Buffer );
+
+ //
+ // Calculate the target relative name. This is simply the complete
+ // name minus the VcbName and the leading backslash.
+ //
+
+ FullName.Buffer = CompleteName.Buffer + ( VcbName.Length / sizeof(WCHAR) ) + 1;
+ FullName.Length = CompleteName.Length -
+ ( (PCHAR)FullName.Buffer - (PCHAR)CompleteName.Buffer );
+
+ //
+ // Calculate the target directory relative name. This the the target
+ // full name, minus the last component of the name.
+ //
+
+ pTrailingSlash = FullName.Buffer + FullName.Length / sizeof(WCHAR) - 1;
+ for ( i = 0; i < FullName.Length ; i += sizeof(WCHAR) ) {
+ if ( *pTrailingSlash == L'\\' ) {
+ break;
+ }
+ --pTrailingSlash;
+ }
+
+
+ Path.Buffer = FullName.Buffer;
+
+ if ( i == FullName.Length ) {
+
+ //
+ // If no trailing slash was found, the the target path is the
+ // root directory.
+ //
+
+ Path.Length = 0;
+
+ } else {
+
+ Path.Length = (PCHAR)pTrailingSlash - (PCHAR)FullName.Buffer;
+
+ }
+
+#if 0
+ Iosb.Status = CrackPath(
+ &CompleteName,
+ &Drive,
+ &DriveLetter,
+ &Server,
+ &Volume,
+ &Path,
+ &FileName,
+ &FullName );
+#endif
+
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ NULL,
+ &Path,
+ &PathIsAFile );
+
+ if ( !NT_SUCCESS( Iosb.Status) ) {
+
+ // The directory containing the file does not exist
+
+ return(Iosb);
+ }
+
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ NULL,
+ &FullName,
+ &FullNameIsAFile );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ FullNameExists = FALSE;
+ Iosb.Information = FILE_DOES_NOT_EXIST;
+ } else {
+ FullNameExists = TRUE;
+ Iosb.Information = 0;
+ }
+
+ DebugTrace( 0, Dbg, "FullNameExists = %08lx\n", FullNameExists);
+ DebugTrace( 0, Dbg, "FullNameIsAFile = %08lx\n", FullNameIsAFile);
+
+ try {
+ UNICODE_STRING TargetPath;
+
+ //
+ // Find the FCB for this file. If the FCB exists, we get a
+ // referenced pointer. Otherwise a new FCB is created.
+ // The file is the complete path minus the target filename.
+ //
+
+ TargetPath = CompleteName;
+
+ Fcb = NwFindFcb( IrpContext->pScb, Vcb, &TargetPath, Dcb );
+
+ //
+ // Now create the ICB.
+ //
+
+ *Icb = NwCreateIcb( NW_NTC_ICB, Fcb );
+
+ (*Icb)->FileObject = IrpSp->FileObject;
+ NwSetFileObject( IrpSp->FileObject, Fcb->NonPagedFcb, *Icb );
+ (*Icb)->Exists = FullNameExists;
+ (*Icb)->IsAFile = FullNameIsAFile;
+
+ try_return(Iosb.Status = STATUS_SUCCESS);
+
+try_exit: NOTHING;
+
+ } finally {
+
+
+ if ( AbnormalTermination() || !NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // Failed to create
+ //
+
+ if ( *Icb != NULL ) {
+ NwDeleteIcb( NULL, *Icb );
+ *Icb = NULL;
+ }
+ }
+ }
+
+ DebugTrace(-1, Dbg, "OpenRenameTarget\n", Iosb.Status);
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreatePrintJob(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb,
+ PUNICODE_STRING DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routines create a new directory.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote print queue.
+
+ Icb - A pointer to the newly created ICB.
+
+ DriveName - LPTx
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ ANSI_STRING ODriveName;
+ static CHAR LptName[] = "LPT" ;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the print queue name is correct.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 ) {
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+ return( Iosb );
+ }
+
+ //
+ // Send a create queue job packet, and wait the response.
+ //
+
+ if ((DriveName->Length == 0 ) ||
+ (!NT_SUCCESS(RtlUnicodeStringToOemString( &ODriveName, DriveName, TRUE )))) {
+ //
+ // if we dont have a name, use the string "LPT". we do this because
+ // some printers insist on a name.
+ //
+
+ RtlInitString(&ODriveName, LptName);
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sd_ddw_b_r_bbwwww_x-x_", // Format string
+ NCP_ADMIN_FUNCTION, NCP_CREATE_QUEUE_JOB,
+ Vcb->Specific.Print.QueueId,// Queue ID
+ 6, // Skip bytes
+ 0xffffffff, // Target Server ID number
+ 0xffffffff, 0xffff, // Target Execution time
+ 11, // Skip bytes
+ 0x00, // Job Control Flags
+ 26, // Skip bytes
+ ODriveName.Buffer, ODriveName.Length, // Description
+ 50 - ODriveName.Length , // Description pad
+ 0, // Version number
+ 8, // Tab Size
+ 1, // Number of copies
+ NwPrintOptions, // Control Flags
+ 0x3C, // Maximum lines BUGBUG
+ 0x84, // Maximum characters BUGBUG
+ 22, // Skip bytes
+ &IrpContext->pScb->UserName, 12, // Banner Name
+ &Vcb->ShareName, 12, // Header Name
+ 1+14+80 // null last string & skip rest of client area
+ );
+
+ //
+ // free the string if it was allocated
+ //
+ if (ODriveName.Buffer != LptName)
+ RtlFreeAnsiString(&ODriveName);
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_w_r",
+ 22,
+ &Icb->JobId,
+ 18,
+ Icb->Handle, sizeof(Icb->Handle) );
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+
+ Fcb = Icb->SuperType.Fcb;
+
+ Fcb->NonPagedFcb->Attributes = 0;
+ Fcb->CreationDate = 0;
+ Fcb->LastAccessDate = 0;
+ Fcb->LastModifiedDate = 0;
+ Fcb->LastModifiedTime = 0;
+
+ Icb->HasRemoteHandle = TRUE;
+ Icb->IsPrintJob = TRUE;
+ Icb->ActuallyPrinted = FALSE;
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_CREATED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+ }
+
+
+ return( Iosb );
+}
+
+VOID
+CloseFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB pIcb
+ )
+/*++
+
+Routine Description:
+
+ This routines closes an opened file.
+
+Arguments:
+
+ pIrpContext - Supplies all the information
+
+ pIcb - A pointer to the newly created ICB.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ pIcb->Handle, 6 );
+}
+
diff --git a/private/nw/rdr/create4.c b/private/nw/rdr/create4.c
new file mode 100644
index 000000000..c0e044be0
--- /dev/null
+++ b/private/nw/rdr/create4.c
@@ -0,0 +1,2075 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ create4.c
+
+Abstract:
+
+ This implements the NDS create routines.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+//
+// Pageable.
+//
+
+#pragma alloc_text( PAGE, NdsCreateTreeScb )
+#pragma alloc_text( PAGE, ConnectBinderyVolume )
+#pragma alloc_text( PAGE, HandleVolumeAttach )
+#pragma alloc_text( PAGE, NdsGetDsObjectFromPath )
+#pragma alloc_text( PAGE, NdsVerifyObject )
+#pragma alloc_text( PAGE, NdsVerifyContext )
+#pragma alloc_text( PAGE, NdsMapObjectToServerShare )
+
+//
+// Not page-able:
+//
+// NdsSelectConnection (holds a spin lock)
+//
+
+NTSTATUS
+NdsSelectConnection(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTreeName,
+ PUNICODE_STRING puUserName,
+ PUNICODE_STRING puPassword,
+ BOOL DeferredLogon,
+ BOOL UseBinderyConnections,
+ PNONPAGED_SCB *ppNpScb
+)
+/*++
+
+Routine Description:
+
+ Find a nearby tree connection point for the given tree.
+
+ DeferredLogon tells us whether or not we need to
+ initiate a login/authenticate exchange yet. If we have
+ credentials to a tree, we are NOT allowed to hand off
+ a connection that has not been logged in because the view
+ of the tree may be different from what it is supposed to
+ be.
+
+ UseBinderyConnections tells us whether or not we want
+ to return bindery authenticated connections as valid
+ nds browse points.
+
+Return Value:
+
+ Scb to a server that belongs to the tree we want.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
+
+ PLOGON pLogon;
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+
+ PNONPAGED_SCB pFirstNpScb, pNextNpScb;
+ PNONPAGED_SCB pFoundNpScb = NULL;
+ PSCB pScb;
+ LARGE_INTEGER Uid;
+
+ PNONPAGED_SCB pOriginalNpScb;
+ PSCB pOriginalScb;
+
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ BOOL PasswordExpired = FALSE;
+
+ //
+ // Save the original server pointers.
+ //
+
+ pOriginalNpScb = pIrpContext->pNpScb;
+ pOriginalScb = pIrpContext->pScb;
+
+ Uid = pIrpContext->Specific.Create.UserUid;
+
+ //
+ // Determine if we need a guest browse connection.
+ //
+
+ if ( DeferredLogon ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( puTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if ( ( pNdsContext->Credential != NULL ) &&
+ ( pNdsContext->CredentialLocked == FALSE ) ) {
+
+ DebugTrace( 0, Dbg, "Forcing authenticated browse to %wZ.\n", puTreeName );
+ DeferredLogon = FALSE;
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+ }
+ }
+
+ //
+ // Start at the head of the SCB list.
+ //
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ if ( ScbQueue.Flink == &ScbQueue ) {
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ ScbQueueEntry = ScbQueue.Flink;
+ pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+ pNextNpScb = pFirstNpScb;
+
+ //
+ // Leave the first SCB referenced since we need it to
+ // be there for when we walk all the way around the list.
+ //
+
+ NwReferenceScb( pFirstNpScb );
+ NwReferenceScb( pNextNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ while ( TRUE ) {
+
+ //
+ // Check to see if the SCB we have is in the correct tree
+ // and is usable. Make sure we skip over the permanent
+ // npscb since it isn't a tree connection. The current
+ // SCB is always referenced while we're in here.
+ //
+
+ if ( pNextNpScb->pScb ) {
+
+ pScb = pNextNpScb->pScb;
+
+ if ( RtlEqualUnicodeString( puTreeName, &pScb->NdsTreeName, TRUE ) &&
+ Uid.QuadPart == pScb->UserUid.QuadPart ) {
+
+ pIrpContext->pNpScb = pNextNpScb;
+ pIrpContext->pScb = pNextNpScb->pScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ switch ( pNextNpScb->State ) {
+
+ case SCB_STATE_RECONNECT_REQUIRED:
+
+ //
+ // Reconnect to the server. This is not
+ // a valid path for an anonymous create,
+ // so there's no chance that we'll get
+ // a name collision.
+ //
+
+ Status = ConnectToServer( pIrpContext, NULL );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ pNextNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+
+ case SCB_STATE_LOGIN_REQUIRED:
+
+ //
+ // See if we can login if requested.
+ //
+
+ if ( !DeferredLogon ) {
+
+ Status = DoNdsLogon( pIrpContext, puUserName, puPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ //
+ // If we get a warning from this, we need to return it!
+ //
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ //
+ // Do we have to re-license the connection?
+ //
+
+ if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ break;
+ }
+ }
+
+ pNextNpScb->State = SCB_STATE_IN_USE;
+ }
+
+ case SCB_STATE_IN_USE:
+
+ if ( pNextNpScb->State == SCB_STATE_IN_USE ) {
+
+ if ( ( !UseBinderyConnections ) &&
+ ( pNextNpScb->pScb->UserName.Length != 0 ) ) {
+
+ //
+ // We may not want to use a connection that has been
+ // bindery authenticated to read the NDS tree because
+ // we don't have a way to validate that the NDS and
+ // bindery users are the same.
+ //
+
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ //
+ // Verify that we have security rights to this server.
+ //
+
+ Status = CheckScbSecurity( pIrpContext,
+ pNextNpScb->pScb,
+ puUserName,
+ puPassword,
+ ( BOOLEAN )DeferredLogon );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ //
+ // Check SCB security might return with state login required.
+ //
+
+ if ( ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) &&
+ ( !DeferredLogon ) ) {
+
+ Status = DoNdsLogon( pIrpContext, puUserName, puPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ pNextNpScb->State = SCB_STATE_IN_USE;
+ }
+
+ } else {
+
+ //
+ // If we picked up an already good SCB and the
+ // login was deferred, set success and continue.
+ //
+
+ ASSERT( DeferredLogon == TRUE );
+ Status = STATUS_SUCCESS;
+ }
+
+ pFoundNpScb = pNextNpScb;
+ DebugTrace( 0, Dbg, "NdsSelectConnection: NpScb = %lx\n", pFoundNpScb );
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ if ( pFoundNpScb ) {
+ ASSERT( NT_SUCCESS( Status ) );
+ break;
+ }
+
+ if ( Status == STATUS_WRONG_PASSWORD ||
+ Status == STATUS_NO_SUCH_USER ) {
+ NwDereferenceScb( pNextNpScb );
+ break;
+ }
+
+ //
+ // Restore the server pointers.
+ //
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalScb;
+
+ }
+ }
+
+ //
+ // Otherwise, get the next one in the list. Don't
+ // forget to skip the list head.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ NwDereferenceScb( pNextNpScb );
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if ( pNextNpScb == pFirstNpScb ) {
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ Status = STATUS_BAD_NETWORK_PATH;
+ break;
+ }
+
+ //
+ // Otherwise, reference this SCB and continue.
+ //
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ }
+
+ NwDereferenceScb( pFirstNpScb );
+ *ppNpScb = pFoundNpScb;
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( PasswordExpired ) ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsCreateTreeScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN OUT PSCB *ppScb,
+ IN PUNICODE_STRING puTree,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN DeferredLogon,
+ IN BOOLEAN DeleteOnClose
+)
+/*++
+
+Description:
+
+ Given a tree name, find us a connection point to the tree. This is
+ done by getting the server addresses out of the bindery and looking
+ up the names of the servers for those addresses.
+
+ When we are all done we need to return the preferred connection
+ point in ppScb.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ ppScb - pointer to a pointer to the scb that we want
+ puTree - tree we want to talk to
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ PLARGE_INTEGER pUid;
+ PNONPAGED_SCB pNpExistingScb;
+
+ UNICODE_STRING UidServerName;
+ PSCB pTreeScb = NULL;
+
+ PSCB pNearestTreeScb = NULL;
+ PNONPAGED_SCB pNpNearestTreeScb = NULL;
+
+ PSCB pNearbyScb = NULL;
+ BOOLEAN fOnNearbyQueue = FALSE;
+ PIRP_CONTEXT pExtraIrpContext = NULL;
+
+ UNICODE_STRING ScanTreeName;
+ WCHAR ScanBuffer[NDS_TREE_NAME_LEN + 2];
+ int i;
+
+ IPXaddress DirServerAddress;
+ CHAR DirServerName[MAX_SERVER_NAME_LENGTH];
+ ULONG dwLastOid = (ULONG)-1;
+
+ UNICODE_STRING CredentialName;
+ PUNICODE_STRING puConnectName;
+
+ PAGED_CODE();
+
+ UidServerName.Buffer = NULL;
+
+ //
+ // Make sure the tree name is reasonable, first.
+ //
+
+ if ( ( !puTree ) ||
+ ( !puTree->Length ) ||
+ ( puTree->Length / sizeof( WCHAR ) ) > NDS_TREE_NAME_LEN ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // If this is an extended credential create, munge the name.
+ //
+
+ RtlInitUnicodeString( &CredentialName, NULL );
+
+ if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
+ ( !IsCredentialName( puTree ) ) ) {
+
+ Status = BuildExCredentialServerName( puTree,
+ puUserName,
+ &CredentialName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ puConnectName = &CredentialName;
+
+ } else {
+
+ puConnectName = puTree;
+ }
+
+ //
+ // First check to see if we already have a connection
+ // to this tree that we can use... If so, this will
+ // leave the irp context pointed at that server for us.
+ //
+ // This time around, don't use bindery authenticated
+ // connections to browse the tree.
+ //
+
+ Status = NdsSelectConnection( pIrpContext,
+ puConnectName,
+ puUserName,
+ puPassword,
+ DeferredLogon,
+ FALSE,
+ &pNpExistingScb );
+
+ if ( NT_SUCCESS( Status ) && pNpExistingScb ) {
+ *ppScb = pNpExistingScb->pScb;
+ ASSERT( *ppScb != NULL );
+ ASSERT( NT_SUCCESS( Status ) );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If there was an authentication failure, bail out.
+ //
+
+ if ( Status == STATUS_NO_SUCH_USER ||
+ Status == STATUS_WRONG_PASSWORD ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Otherwise, we need to select a dir server. To do this,
+ // we have to look up dir server names by address. To do
+ // this we create an SCB for synchronization with the name
+ // *tree*, which isn't a valid server name.
+ //
+
+ ScanTreeName.Length = sizeof( WCHAR );
+ ScanTreeName.MaximumLength = sizeof( ScanBuffer );
+ ScanTreeName.Buffer = ScanBuffer;
+
+ ScanBuffer[0] = L'*';
+ RtlAppendUnicodeStringToString( &ScanTreeName, puTree );
+ ScanBuffer[( ScanTreeName.Length / sizeof( WCHAR ) )] = L'*';
+ ScanTreeName.Length += sizeof( WCHAR );
+
+ //
+ // Now make it a uid server name.
+ //
+
+ Status = MakeUidServer( &UidServerName,
+ &pIrpContext->Specific.Create.UserUid,
+ &ScanTreeName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ NwFindScb( &pTreeScb,
+ pIrpContext,
+ &UidServerName,
+ &ScanTreeName );
+
+ if ( !pTreeScb ) {
+ DebugTrace( 0, Dbg, "Failed to get a tree scb for synchronization.\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get a nearby server connection and prepare to
+ // do the bindery scan for tree connection points.
+ // Don't forget to copy the user uid for security.
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pExtraIrpContext,
+ pTreeScb->pNpScb ) ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ pExtraIrpContext->Specific.Create.UserUid.QuadPart =
+ pIrpContext->Specific.Create.UserUid.QuadPart;
+
+ //
+ // Append a wildcard to the tree name for the bindery scan.
+ //
+
+ ScanTreeName.Length = 0;
+ ScanTreeName.MaximumLength = sizeof( ScanBuffer );
+ ScanTreeName.Buffer = ScanBuffer;
+
+ RtlCopyUnicodeString( &ScanTreeName, puTree );
+
+ i = ScanTreeName.Length / sizeof( WCHAR );
+
+ while( i <= NDS_TREE_NAME_LEN ) {
+ ScanBuffer[i++] = L'_';
+ }
+
+ ScanBuffer[NDS_TREE_NAME_LEN] = L'*';
+ ScanTreeName.Length = (NDS_TREE_NAME_LEN + 1) * sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, "Scanning for NDS tree %wZ.\n", puTree );
+
+ //
+ // Now we lookup the dir server addresses in the bindery and
+ // try to make dir server connections.
+ //
+
+ while ( TRUE ) {
+
+ if ( ( pNearbyScb ) && ( !fOnNearbyQueue ) ) {
+
+ //
+ // Get back to the head of the nearby server so we can continue
+ // looking for dir servers. If the nearby server is no good anymore,
+ // dereference the connection and set the nearby scb pointer to
+ // NULL. This will cause us to get a new nearby server when we
+ // continue.
+ //
+
+ NwAppendToQueueAndWait( pExtraIrpContext );
+
+ if ( !( ( pNearbyScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
+ ( pNearbyScb->pNpScb->State == SCB_STATE_IN_USE ) ) ) {
+
+ NwDequeueIrpContext( pExtraIrpContext, FALSE );
+ NwDereferenceScb( pNearbyScb->pNpScb );
+ pNearbyScb = NULL;
+
+ //
+ // Don't restart the search. If our bindery server went down in
+ // the middle of a connect, the connect will fail and that's ok.
+ // If we restart the search we can end up in this loop forever.
+ //
+
+ } else {
+
+ fOnNearbyQueue = TRUE;
+ }
+
+ }
+
+ //
+ // Get a bindery server to talk to if we don't have one. This may
+ // be our first time through this loop, or our server may have
+ // gone bad (see above).
+ //
+ // Optimization: What if this CreateScb returns a valid dir server
+ // for the tree we are looking for? We should use it!!
+ //
+
+ if ( !pNearbyScb ) {
+
+ Status = CreateScb( &pNearbyScb,
+ pExtraIrpContext,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( pExtraIrpContext->pNpScb == pNearbyScb->pNpScb );
+ ASSERT( pExtraIrpContext->pScb == pNearbyScb );
+
+ NwAppendToQueueAndWait( pExtraIrpContext );
+ fOnNearbyQueue = TRUE;
+
+ }
+
+ //
+ // Look up the dir server address from our nearby server.
+ //
+
+ Status = ExchangeWithWait( pExtraIrpContext,
+ SynchronousResponseCallback,
+ "SdwU",
+ NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
+ dwLastOid,
+ OT_DIRSERVER,
+ &ScanTreeName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // We're out of options for dir servers.
+ //
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ break;
+ }
+
+ Status = ParseResponse( pExtraIrpContext,
+ pExtraIrpContext->rsp,
+ pExtraIrpContext->ResponseLength,
+ "Nd_r",
+ &dwLastOid,
+ sizeof( WORD ),
+ DirServerName,
+ MAX_SERVER_NAME_LENGTH );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ Status = ExchangeWithWait ( pExtraIrpContext,
+ SynchronousResponseCallback,
+ "Swbrbp",
+ NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
+ OT_DIRSERVER,
+ 0x30,
+ DirServerName,
+ MAX_SERVER_NAME_LENGTH,
+ 1, // Segment number
+ NET_ADDRESS_PROPERTY );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No net address property for this dir server.\n", 0 );
+ continue;
+ }
+
+ Status = ParseResponse( pExtraIrpContext,
+ pExtraIrpContext->rsp,
+ pExtraIrpContext->ResponseLength,
+ "Nr",
+ &DirServerAddress,
+ sizeof(TDI_ADDRESS_IPX) );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Couldn't parse net address property for this dir server.\n", 0 );
+ continue;
+ }
+
+ //
+ // We know the address of the dir server, so do an anonymous
+ // create to it. Use the original irp context so the uid is
+ // correct. Note that we have to dequeue from the nearby scb
+ // in case we are referred to that server!
+ //
+
+ NwDequeueIrpContext( pExtraIrpContext, FALSE );
+ fOnNearbyQueue = FALSE;
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &pNearestTreeScb,
+ pIrpContext,
+ NULL,
+ &DirServerAddress,
+ puUserName,
+ puPassword,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Status == STATUS_NO_SUCH_USER ||
+ Status == STATUS_WRONG_PASSWORD ||
+ Status == STATUS_ACCESS_DENIED ||
+ Status == STATUS_ACCOUNT_DISABLED ||
+ Status == STATUS_LOGIN_TIME_RESTRICTION ||
+ Status == STATUS_REMOTE_SESSION_LIMIT ||
+ Status == STATUS_CONNECTION_COUNT_LIMIT ||
+ Status == STATUS_NETWORK_CREDENTIAL_CONFLICT ||
+ Status == STATUS_PASSWORD_EXPIRED ) {
+ break;
+ }
+
+ continue;
+ }
+
+ //
+ // If the server we got back was bindery authenticated,
+ // it is NOT a valid dir server for us to use (yet)!!
+ //
+
+ if ( pNearestTreeScb->UserName.Length != 0 ) {
+
+ Status = STATUS_ACCESS_DENIED;
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pNearestTreeScb->pNpScb );
+
+ continue;
+ }
+
+ //
+ // Otherwise, we're golden. Break out of here!
+ //
+
+ DebugTrace( 0, Dbg, "Dir server: %wZ\n", &pNearestTreeScb->UidServerName );
+ *ppScb = pNearestTreeScb;
+ ASSERT( NT_SUCCESS( Status ) );
+ break;
+
+ }
+
+ //
+ // We have been wholly unable to get a browse connection
+ // to this tree. Try again but this time allow the use
+ // of connections that are bindery authenticated. We don't
+ // need the nearby server anymore.
+ //
+
+ if ( pNearbyScb ) {
+
+ NwDequeueIrpContext( pExtraIrpContext, FALSE );
+ NwDereferenceScb( pNearbyScb->pNpScb );
+ }
+
+ if ( ( Status != STATUS_SUCCESS ) &&
+ ( Status != STATUS_NO_SUCH_USER ) &&
+ ( Status != STATUS_WRONG_PASSWORD ) &&
+ ( Status != STATUS_ACCESS_DENIED ) &&
+ ( Status != STATUS_ACCOUNT_DISABLED ) &&
+ ( Status != STATUS_LOGIN_TIME_RESTRICTION ) &&
+ ( Status != STATUS_REMOTE_SESSION_LIMIT ) &&
+ ( Status != STATUS_CONNECTION_COUNT_LIMIT ) &&
+ ( Status != STATUS_NETWORK_CREDENTIAL_CONFLICT ) &&
+ ( Status != STATUS_PASSWORD_EXPIRED ) ) {
+
+ Status = NdsSelectConnection( pIrpContext,
+ puConnectName,
+ puUserName,
+ puPassword,
+ DeferredLogon,
+ TRUE,
+ &pNpExistingScb );
+
+ if ( NT_SUCCESS( Status ) && pNpExistingScb ) {
+ *ppScb = pNpExistingScb->pScb;
+ ASSERT( *ppScb != NULL );
+ }
+ }
+
+ExitWithCleanup:
+
+ //
+ // Clean up and bail.
+ //
+
+ if ( pExtraIrpContext ) {
+ NwFreeExtraIrpContext( pExtraIrpContext );
+ }
+
+ if ( UidServerName.Buffer != NULL ) {
+ FREE_POOL( UidServerName.Buffer );
+ }
+
+ if ( pTreeScb ) {
+ NwDereferenceScb( pTreeScb->pNpScb );
+ }
+
+ if ( CredentialName.Buffer ) {
+ FREE_POOL( CredentialName.Buffer );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+ConnectBinderyVolume(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+)
+/*++
+
+Description:
+
+ Given a server name and a volume, try to connect the volume.
+ This is used in QueryPath to pre-connect a volume.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb;
+ PVCB pVcb;
+
+ PAGED_CODE();
+
+ //
+ // Try making a server connection with this name.
+ //
+
+ Status = CreateScb( &pScb,
+ pIrpContext,
+ puServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, "Bindery volume connect got server %wZ\n", puServerName );
+
+ //
+ // If we succeeded, do a standard bindery volume attach.
+ //
+
+ pVcb = NwFindVcb( pIrpContext,
+ puVolumeName,
+ RESOURCETYPE_ANY,
+ 0,
+ FALSE,
+ FALSE );
+
+ if ( pVcb == NULL ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+
+ } else {
+
+ //
+ // We should not have jumped servers since this was explicit.
+ //
+
+ ASSERT( pScb == pIrpContext->pScb );
+
+ //
+ // Remove NwFindVcb reference. Don't supply an IrpContext
+ // so the Vcb doesn't get destroyed immediately after we just
+ // created it because no-one else has it referenced.
+ //
+
+ NwDereferenceVcb( pVcb, NULL, FALSE );
+ DebugTrace( 0, Dbg, "Bindery volume connect got volume %wZ\n", puVolumeName );
+ }
+
+ NwDereferenceScb( pScb->pNpScb );
+ return Status;
+
+}
+
+NTSTATUS
+HandleVolumeAttach(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+)
+/*++
+
+Description:
+
+ This function is only callable from the QUERY_PATH code path!
+
+ This functions takes a server name and volume name from
+ QueryPath() and resolves it into a server/volume connection.
+ The server/volume name can be plain or can refer to an
+ nds tree and the nds path to a volume object.
+
+ In the nds case, we only verify that the volume object exists.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ puServerName - server name or nds tree name
+ puVolumeName - volume name or nds path to volume object
+
+--*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb;
+
+ UNICODE_STRING uDsObject;
+ DWORD dwVolumeOid, dwObjectType;
+
+ PAGED_CODE();
+
+ //
+ // Try the bindery server/volume case first.
+ //
+
+ Status = ConnectBinderyVolume( pIrpContext,
+ puServerName,
+ puVolumeName );
+ if ( NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ if ( Status == STATUS_NETWORK_UNREACHABLE ) {
+
+ // IPX is not bound to anything that is currently
+ // up (which means it's probably bound only to the
+ // RAS WAN wrapper). Don't waste time looking for
+ // a ds tree.
+ //
+
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // See if this is a tree name and get a ds connection.
+ //
+
+ pIrpContext->Specific.Create.NdsCreate = TRUE;
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &pScb,
+ puServerName,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // If we have a tree, resolve the volume object.
+ // BUGBUG: We should actually check to see if we
+ // already have a connection to this object before
+ // we hit the ds.
+ //
+
+ Status = NdsGetDsObjectFromPath( pIrpContext,
+ &uDsObject );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ return Status;
+ }
+
+ Status = NdsVerifyObject( pIrpContext, // irp context for the request
+ &uDsObject, // path to volume object
+ TRUE, // allow a server jump
+ DEFAULT_RESOLVE_FLAGS, // resolver flags
+ &dwVolumeOid, // volume oid from the ds
+ &dwObjectType ); // volume or print queue
+
+ //
+ // We may have jumped servers in the VerifyObject code,
+ // so just make sure we dereference the correct server.
+ //
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetDsObjectFromPath(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING puDsObject
+)
+/*++
+
+Description:
+
+ Take the full path from the create irp context and
+ extract out the ds path of the desired object.
+
+ The supplied unicode string shouldn't have a buffer;
+ it will be set up to point into the user's buffer
+ referred to by the irp context.
+
+Arguments:
+
+ pIrpContext - an irp context from a create path request
+ puDsObject - unicode string that will refer to the correct ds path
+
+--*/
+{
+
+ DWORD dwPathSeparators;
+ USHORT NewHead;
+
+ PAGED_CODE();
+
+ //
+ // The VolumeName is one of the following:
+ //
+ // \X:\Server\Volume.Object.Path
+ // \Server\Volume.Object.Path
+ //
+
+ *puDsObject = pIrpContext->Specific.Create.VolumeName;
+
+ //
+ // Skip the leading slash.
+ //
+
+ puDsObject->Length -= sizeof( WCHAR );
+ puDsObject->Buffer += 1;
+
+ //
+ // How many more are there to overcome?
+ //
+
+ NewHead = 0;
+ dwPathSeparators = pIrpContext->Specific.Create.DriveLetter ? 2 : 1;
+
+ while ( NewHead < puDsObject->Length &&
+ dwPathSeparators ) {
+
+ if ( puDsObject->Buffer[NewHead/sizeof(WCHAR)] == OBJ_NAME_PATH_SEPARATOR ) {
+ dwPathSeparators--;
+ }
+
+ NewHead += sizeof( WCHAR );
+ }
+
+ if ( dwPathSeparators ||
+ NewHead == puDsObject->Length) {
+
+ //
+ // Something wasn't formed right in the volume name.
+ //
+
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ puDsObject->Length -= NewHead;
+ puDsObject->Buffer += NewHead/sizeof(WCHAR);
+
+ //
+ // If there is a leading dot, skip it.
+ //
+
+ if ( puDsObject->Buffer[0] == L'.' ) {
+
+ puDsObject->Length -= sizeof( WCHAR );
+ puDsObject->Buffer += 1;
+ }
+
+ puDsObject->MaximumLength = puDsObject->Length;
+
+ DebugTrace( 0, Dbg, "DS object: %wZ\n", puDsObject );
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NdsVerifyObject(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puDsObject,
+ IN BOOLEAN fAllowServerJump,
+ IN DWORD dwResolverFlags,
+ OUT PDWORD pdwDsOid,
+ OUT PDWORD pdwObjectType
+)
+/*++
+
+Description:
+
+ This function verifies that a ds path refers to a volume
+ object, print queue, or a dir map. It returns the oid
+ of the object.
+
+ If fAllowServerJump is set to false, this simply looks up
+ the oid on the current server but doesn't verify the object
+ type. This routine checks all appropriate contexts for the
+ object, unlike ResolveNameKm.
+
+Parameters:
+
+ pIrpContext - irp context for this request, pointed to the ds server
+ puDsObject - path to the object in the ds
+ fAllowServerJump - allow a server jump to take place
+ pdwDsOid - destination of the ds oid of the object
+ pdwObjectType - NDS_OBJECTTYPE_VOLUME, NDS_OBJECTTYPE_QUEUE, or NDS_OBJECTTYPE_DIRMAP
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ PNDS_SECURITY_CONTEXT pCredentials = NULL;
+ PUNICODE_STRING puAppendableContext;
+
+ UNICODE_STRING uFdnObject;
+ WCHAR FdnObject[MAX_NDS_NAME_CHARS];
+
+ PLOGON pLogon;
+ PSCB pScb;
+ USHORT i;
+
+ LOCKED_BUFFER NdsRequest;
+ DWORD dwObjectOid, dwObjectType;
+
+ UNICODE_STRING uVolume;
+ UNICODE_STRING uQueue;
+ UNICODE_STRING uDirMap;
+
+ UNICODE_STRING uReplyString;
+ WCHAR ReplyBuffer[32];
+ BOOLEAN fHoldingCredentialList = FALSE;
+ BOOLEAN fPartiallyDistinguished = FALSE;
+
+ PAGED_CODE();
+
+ NdsRequest.pRecvBufferVa = NULL;
+
+ //
+ // Get the user credentials.
+ //
+
+ pScb = pIrpContext->pNpScb->pScb;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // Get the credential. We don't care if it's locked or
+ // not since we're just querying the ds.
+ //
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+ ASSERT( pCredentials != NULL );
+ fHoldingCredentialList = TRUE;
+ }
+
+ }
+
+ //
+ // Check to see if it's at least partially distinguished already.
+ //
+
+ i = 0;
+ while (i < puDsObject->Length / sizeof( WCHAR ) ) {
+
+ if ( puDsObject->Buffer[i++] == L'.' ) {
+ fPartiallyDistinguished = TRUE;
+ }
+ }
+
+ //
+ // If it's partially distinguished, try it without the context first.
+ //
+
+ if ( fPartiallyDistinguished ) {
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ puDsObject,
+ &dwObjectOid,
+ fAllowServerJump,
+ dwResolverFlags );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", puDsObject );
+ goto GetObjectType;
+ }
+ }
+
+ //
+ // If that failed, or if it wasn't partially distinguished,
+ // see if there's a current context we can append.
+ //
+
+ if ( ( pCredentials ) &&
+ ( pCredentials->CurrentContext.Length ) ) {
+
+ if ( ( puDsObject->Length + pCredentials->CurrentContext.Length ) < sizeof( FdnObject ) ) {
+
+ //
+ // Append the context.
+ //
+
+ uFdnObject.MaximumLength = sizeof( FdnObject );
+ uFdnObject.Buffer = FdnObject;
+
+ RtlCopyMemory( FdnObject, puDsObject->Buffer, puDsObject->Length );
+ uFdnObject.Length = puDsObject->Length;
+
+ if ( uFdnObject.Buffer[( uFdnObject.Length / sizeof( WCHAR ) ) - 1] == L'.' ) {
+ uFdnObject.Length -= sizeof( WCHAR );
+ }
+
+ if ( pCredentials->CurrentContext.Buffer[0] != L'.' ) {
+ uFdnObject.Buffer[uFdnObject.Length / sizeof( WCHAR )] = L'.';
+ uFdnObject.Length += sizeof( WCHAR );
+ }
+
+ RtlCopyMemory( ((BYTE *)FdnObject) + uFdnObject.Length,
+ pCredentials->CurrentContext.Buffer,
+ pCredentials->CurrentContext.Length );
+
+ uFdnObject.Length += pCredentials->CurrentContext.Length;
+
+ //
+ // Resolve this name.
+ //
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ &uFdnObject,
+ &dwObjectOid,
+ fAllowServerJump,
+ dwResolverFlags );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", &uFdnObject );
+ goto GetObjectType;
+ }
+
+ }
+
+ }
+
+ //
+ // This is not a valid name.
+ //
+
+ DebugTrace( 0, Dbg, "VerifyObject: No ds object to resolve.\n", 0 );
+
+ if ( fHoldingCredentialList ) {
+ NwReleaseCredList( pLogon );
+ fHoldingCredentialList = FALSE;
+ }
+
+ return STATUS_BAD_NETWORK_PATH;
+
+
+GetObjectType:
+
+ if ( fHoldingCredentialList ) {
+ NwReleaseCredList( pLogon );
+ fHoldingCredentialList = FALSE;
+ }
+
+ //
+ // If a server jump is not allowed, we don't need to worry
+ // about getting the object type.
+ //
+
+ if ( !fAllowServerJump ) {
+ dwObjectType = 0;
+ goto CompletedObject;
+ }
+
+ //
+ // Resolve the object and get its information.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ &NdsRequest,
+ "DD",
+ 0,
+ dwObjectOid );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that it's a volume object.
+ //
+
+ RtlInitUnicodeString( &uVolume, VOLUME_ATTRIBUTE );
+ RtlInitUnicodeString( &uQueue, QUEUE_ATTRIBUTE );
+ RtlInitUnicodeString( &uDirMap, DIR_MAP_ATTRIBUTE );
+
+ uReplyString.Length = 0;
+ uReplyString.MaximumLength = sizeof( ReplyBuffer );
+ uReplyString.Buffer = ReplyBuffer;
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_T",
+ sizeof( NDS_RESPONSE_GET_OBJECT_INFO ),
+ &uReplyString );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ dwObjectType = 0;
+
+ if ( !RtlCompareUnicodeString( &uVolume, &uReplyString, FALSE ) ) {
+ dwObjectType = NDS_OBJECTTYPE_VOLUME;
+ } else if ( !RtlCompareUnicodeString( &uQueue, &uReplyString, FALSE ) ) {
+ dwObjectType = NDS_OBJECTTYPE_QUEUE;
+ } else if ( !RtlCompareUnicodeString( &uDirMap, &uReplyString, FALSE ) ) {
+ dwObjectType = NDS_OBJECTTYPE_DIRMAP;
+ }
+
+ if ( !dwObjectType ) {
+
+ DebugTrace( 0, Dbg, "DS object is not a connectable type.\n", 0 );
+ Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+ goto ExitWithCleanup;
+ }
+
+CompletedObject:
+
+ if ( pdwDsOid ) {
+ *pdwDsOid = dwObjectOid;
+ }
+
+ if ( pdwObjectType ) {
+ *pdwObjectType = dwObjectType;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentialList ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( NdsRequest.pRecvBufferVa ) {
+ NdsFreeLockedBuffer( &NdsRequest );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsVerifyContext(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTree,
+ PUNICODE_STRING puContext
+)
+/*++
+
+ Given a context and a tree, verify that the context is a
+ valid container in the tree.
+
+ This call may cause the irpcontex to jump servers to an
+ referred dir server. If so, the scb pointers in the irp
+ context will be updated, the old server will be dereferenced,
+ and the new server will hold the reference for this request.
+
+--*/
+{
+
+ NTSTATUS Status;
+ DWORD dwOid, dwSubordinates;
+ LOCKED_BUFFER NdsRequest;
+ PSCB pScb, pTreeScb;
+ PNONPAGED_SCB pNpScb;
+
+ PAGED_CODE();
+
+ //
+ // Establish a browse connection to the tree we want to query.
+ //
+
+ NdsRequest.pRecvBufferVa = NULL;
+
+ pScb = pIrpContext->pScb;
+ pNpScb = pIrpContext->pNpScb;
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &pTreeScb,
+ puTree,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pTreeScb = NULL;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ puContext,
+ &dwOid,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "NdsVerifyContext: resolve failed.\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ NdsRequest.pRecvBufferVa = NULL;
+ goto ExitWithCleanup;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ &NdsRequest,
+ "DD",
+ 0,
+ dwOid );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that it's a volume object by checking the
+ // third DWORD, which is the subordinate count.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_D",
+ 2 * sizeof( DWORD ),
+ &dwSubordinates );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( !dwSubordinates ) {
+
+ DebugTrace( 0, Dbg, "No subordinates in VerifyContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Success!
+ //
+
+ExitWithCleanup:
+
+ //
+ // We may have jumped servers in the resolve name call,
+ // so make sure we dereference the correct SCB!
+ //
+
+ if ( pTreeScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ }
+
+ //
+ // Restore the connection to the original server.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pNpScb;
+
+ if ( NdsRequest.pRecvBufferVa ) {
+ NdsFreeLockedBuffer( &NdsRequest );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NdsMapObjectToServerShare(
+ PIRP_CONTEXT pIrpContext,
+ PSCB *ppScb,
+ PUNICODE_STRING puServerSharePath,
+ BOOLEAN CreateTreeConnection,
+ PDWORD pdwObjectId
+)
+/*++
+
+Description:
+
+ This function takes a pointer to a tree scb and an irp
+ context for a create request. It looks up the ds object
+ from the create request in the ds and maps it to
+ the appropriate server/share duple.
+
+ The FullPathName and VolumeName strings in the create
+ section of the irp context are updated and a connection
+ to the real host server is established so that the
+ create request can continue as desired.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ UNICODE_STRING uServerAttribute;
+ UNICODE_STRING uVolumeAttribute;
+ UNICODE_STRING uQueueAttribute;
+ UNICODE_STRING uPathAttribute;
+
+ UNICODE_STRING uHostServer;
+ UNICODE_STRING uRealServerName;
+ UNICODE_STRING uHostVolume;
+ UNICODE_STRING uHostPath;
+ UNICODE_STRING uIntermediateVolume;
+
+ UNICODE_STRING uDsObjectPath;
+ DWORD dwObjectOid, dwObjectType, dwDirMapType;
+
+ DWORD dwTotalPathLen;
+ USHORT usSrv;
+ PSCB pOldScb, pNewServerScb;
+
+ UNICODE_STRING UserName, Password;
+ ULONG ShareType;
+
+ PAGED_CODE();
+
+ //
+ // Set up strings and buffers.
+ //
+
+ RtlInitUnicodeString( &uServerAttribute, HOST_SERVER_ATTRIBUTE );
+ RtlInitUnicodeString( &uVolumeAttribute, HOST_VOLUME_ATTRIBUTE );
+ RtlInitUnicodeString( &uQueueAttribute, HOST_QUEUE_ATTRIBUTE );
+ RtlInitUnicodeString( &uPathAttribute, HOST_PATH_ATTRIBUTE );
+
+ RtlInitUnicodeString( &uHostServer, NULL );
+ RtlInitUnicodeString( &uRealServerName, NULL );
+ RtlInitUnicodeString( &uHostVolume, NULL );
+ RtlInitUnicodeString( &uHostPath, NULL );
+ RtlInitUnicodeString( &uIntermediateVolume, NULL );
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ uHostServer.Buffer = ALLOCATE_POOL( PagedPool, 4 * MAX_NDS_NAME_SIZE );
+
+ if ( !uHostServer.Buffer ) {
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ uHostServer.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ uHostVolume.Buffer = ( PWCHAR )(((BYTE *)uHostServer.Buffer) + MAX_NDS_NAME_SIZE);
+ uHostVolume.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ uHostPath.Buffer = ( PWCHAR )(((BYTE *)uHostVolume.Buffer) + MAX_NDS_NAME_SIZE);
+ uHostPath.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ uIntermediateVolume.Buffer = ( PWCHAR )(((BYTE *)uHostPath.Buffer) + MAX_NDS_NAME_SIZE);
+ uIntermediateVolume.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ //
+ // First get the object id from the ds.
+ //
+
+ Status = NdsGetDsObjectFromPath( pIrpContext, &uDsObjectPath );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ pOldScb = pIrpContext->pScb;
+
+ Status = NdsVerifyObject( pIrpContext,
+ &uDsObjectPath,
+ TRUE, // allow server jumping
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectOid,
+ &dwObjectType );
+
+ //
+ // We may have jumped servers.
+ //
+
+ *ppScb = pIrpContext->pScb;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If this is a dir map, grab the target volume and re-verify
+ // the object for connectability.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ //
+ // First get the volume object and path.
+ //
+
+ Status = NdsReadAttributesKm( pIrpContext,
+ dwObjectOid,
+ &uPathAttribute,
+ &NdsRequest );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the volume path and the directory path.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_____S_ST",
+ sizeof( DWORD ), // completion code
+ sizeof( DWORD ), // iter handle
+ sizeof( DWORD ), // info type
+ sizeof( DWORD ), // attribute count
+ sizeof( DWORD ), // syntax id
+ NULL, // attribute name
+ 3 * sizeof( DWORD ), // unknown
+ &uIntermediateVolume, // ds volume
+ &uHostPath ); // dir map path
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify the target volume object.
+ //
+
+ Status = NdsVerifyObject( pIrpContext,
+ &uIntermediateVolume,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectOid,
+ &dwDirMapType );
+
+ //
+ // We may have jumped servers.
+ //
+
+ *ppScb = pIrpContext->pScb;
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( dwDirMapType == NDS_OBJECTTYPE_VOLUME );
+
+ }
+
+ //
+ // Get the server (for any connectable object).
+ //
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectOid,
+ &uServerAttribute,
+ &uHostServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the host volume or queue.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
+ dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectOid,
+ &uVolumeAttribute,
+ &uHostVolume );
+
+ } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectOid,
+ &uQueueAttribute,
+ &uHostVolume );
+
+ } else {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the actual server name from the X.500 name.
+ //
+
+ Status = NdsGetServerBasicName( &uHostServer,
+ &uRealServerName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Make sure we have enough space in the new buffer to format
+ // the new connect string of \X:\Server\Share\Path,
+ // \LPTX\Server\Share\Path, or \Server\Share\Path.
+ //
+
+ dwTotalPathLen = uRealServerName.Length + uHostVolume.Length;
+ dwTotalPathLen += ( sizeof( L"\\\\" ) - sizeof( L"" ) );
+
+ //
+ // Account for the correct prefix. We count on single character
+ // drive and printer letters here. Again, maybe unwise later on.
+ //
+
+ if ( pIrpContext->Specific.Create.DriveLetter ) {
+
+ if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
+ dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ dwTotalPathLen += ( sizeof( L"X:\\" ) - sizeof( L"" ) );
+
+ } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ dwTotalPathLen += ( sizeof( L"LPT1\\" ) - sizeof( L"" ) );
+
+ } else {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+ }
+
+ //
+ // Count space for the path and filename if present.
+ //
+
+ if ( pIrpContext->Specific.Create.PathName.Length ) {
+ dwTotalPathLen += pIrpContext->Specific.Create.PathName.Length;
+ }
+
+ if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+ dwTotalPathLen += uHostPath.Length;
+ dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) );
+ }
+
+ if ( pIrpContext->Specific.Create.FileName.Length ) {
+ dwTotalPathLen += pIrpContext->Specific.Create.FileName.Length;
+ dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) );
+ }
+
+ if ( dwTotalPathLen > puServerSharePath->MaximumLength ) {
+
+ DebugTrace( 0 , Dbg, "NdsMapObjectToServerShare: Buffer too small.\n", 0 );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // First dequeue the irp context from the dir server we've been
+ // talking to, then make the connect to the new server. We logged
+ // in earlier so this will get us an authenticated connection.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Since it's possible for us to get attaching to a bindery
+ // authenticated resource, we have to dig out the user name
+ // and password for the create call!!
+ //
+
+ ReadAttachEas( pIrpContext->pOriginalIrp,
+ &UserName,
+ &Password,
+ &ShareType,
+ NULL );
+
+ Status = CreateScb( &pNewServerScb,
+ pIrpContext,
+ &uRealServerName,
+ NULL,
+ &UserName,
+ &Password,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( pNewServerScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ NwDereferenceScb( (*ppScb)->pNpScb );
+ *ppScb = pNewServerScb;
+
+ //
+ // Re-query the OID of the print queue object on this server
+ // or it could be wrong. Do not permit any sort of a server
+ // jump this time.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ Status = NdsVerifyObject( pIrpContext,
+ &uDsObjectPath,
+ FALSE,
+ RSLV_CREATE_ID,
+ &dwObjectOid,
+ NULL );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ }
+
+ if ( pdwObjectId ) {
+ *pdwObjectId = dwObjectOid;
+ }
+
+ //
+ // Re-format the path strings in the irp context. The nds share
+ // length tells us how much of the NDS share name is interesting
+ // for getting the directory handle.
+ //
+
+ usSrv = 0;
+ pIrpContext->Specific.Create.dwNdsShareLength = 0;
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length = sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+
+ //
+ // Set the proper prefix for this connect type.
+ //
+
+ if ( pIrpContext->Specific.Create.DriveLetter ) {
+
+ if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'L';
+ usSrv += sizeof( WCHAR );
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'P';
+ usSrv += sizeof( WCHAR );
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'T';
+ usSrv += sizeof( WCHAR );
+ }
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] =
+ pIrpContext->Specific.Create.DriveLetter;
+ usSrv += sizeof( WCHAR );
+
+ if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) {
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L':';
+ usSrv += sizeof( WCHAR );
+ }
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ usSrv += sizeof( WCHAR );
+
+ puServerSharePath->Length = usSrv;
+ }
+
+ //
+ // Append the server name.
+ //
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uRealServerName );
+ usSrv += uRealServerName.Length;
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length += sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+
+ //
+ // Append the volume for volumes or the full ds path to
+ // the print queue for queues.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
+ dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uHostVolume );
+ usSrv += uHostVolume.Length;
+ pIrpContext->Specific.Create.dwNdsShareLength += uHostVolume.Length;
+
+ } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uDsObjectPath );
+ usSrv += uDsObjectPath.Length;
+ pIrpContext->Specific.Create.dwNdsShareLength += uDsObjectPath.Length;
+
+ }
+
+ //
+ // Append the dir map path.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length += sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+ pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR );
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uHostPath );
+ usSrv += uHostPath.Length;
+ pIrpContext->Specific.Create.dwNdsShareLength += uHostPath.Length;
+
+ }
+
+ //
+ // Handle the path and file if they exist.
+ //
+
+ if ( pIrpContext->Specific.Create.PathName.Length ) {
+
+ ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE );
+ RtlAppendUnicodeStringToString( puServerSharePath,
+ &pIrpContext->Specific.Create.PathName );
+ usSrv += pIrpContext->Specific.Create.PathName.Length;
+
+ //
+ // If this is a tree connection, then include the path in
+ // the share name so that the map point is correct.
+ //
+
+ if ( CreateTreeConnection ) {
+ pIrpContext->Specific.Create.dwNdsShareLength +=
+ pIrpContext->Specific.Create.PathName.Length;
+ }
+ }
+
+ if ( pIrpContext->Specific.Create.FileName.Length ) {
+
+ ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE );
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length += sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+
+ RtlAppendUnicodeStringToString( puServerSharePath,
+ &pIrpContext->Specific.Create.FileName );
+ usSrv += pIrpContext->Specific.Create.FileName.Length;
+
+ //
+ // If this is a tree connection, then include the file in
+ // the share name so that the map point is correct.
+ //
+
+ if ( CreateTreeConnection ) {
+ pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR );
+ pIrpContext->Specific.Create.dwNdsShareLength +=
+ pIrpContext->Specific.Create.FileName.Length;
+ }
+ }
+
+ //
+ // Record the object type in the irp context.
+ //
+
+ pIrpContext->Specific.Create.dwNdsObjectType = dwObjectType;
+
+ DebugTrace( 0, Dbg, "DS Object path is %wZ\n", &pIrpContext->Specific.Create.FullPathName );
+ DebugTrace( 0, Dbg, "Resolved path is %wZ\n", puServerSharePath );
+
+ExitWithCleanup:
+
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ FREE_POOL( uHostServer.Buffer );
+ return Status;
+}
diff --git a/private/nw/rdr/crypto.h b/private/nw/rdr/crypto.h
new file mode 100644
index 000000000..0de8e0865
--- /dev/null
+++ b/private/nw/rdr/crypto.h
@@ -0,0 +1,139 @@
+/* crypto.h
+ *
+ * Prototypes and definitions for services in crypto.c
+ *
+ * ported to win nt from win 95 on 6/95
+ * Cory West
+ */
+
+#include <windef.h>
+
+#define CIPHERBLOCKSIZE 8 // size of RC2 block
+#define MAX_RSA_BITS 512 // actually 420
+#define MAX_RSA_BYTES (MAX_RSA_BITS/8)
+
+#define B_PSIZEBITS 210
+#define B_PSIZEWORDS (1 + B_PSIZEBITS/32)
+
+void __cdecl
+GenRandomBytes(
+ BYTE *output,
+ int len
+);
+
+//
+// Generate an 8 byte key from a seed of the given length.
+//
+
+void __cdecl
+GenKey8(
+ BYTE *keyData,
+ int keyDataLen,
+ BYTE key8[8]
+);
+
+void __cdecl
+MD2(
+ BYTE *input,
+ const int inlen,
+ BYTE *output
+);
+
+//
+// RC2 encrypt and decrypt wrappers.
+//
+
+int __cdecl
+CBCEncrypt(
+ BYTE *key, // secret key
+ BYTE const *ivec, // initialization vector, NULL implies zero vector
+ BYTE *const input, // plain text
+ int inlen, // size of plaintext
+ BYTE *const output, // encrypted text
+ int *outlen, // OUTPUT: size of encrypted text
+ const int checksumlen // size of checksum, if 0 no checksum is used
+);
+
+int __cdecl
+CBCDecrypt(
+ BYTE *key, // secret key
+ BYTE *ivec, // initialization vector, null ptr implies zero vector
+ BYTE *input, // encrypted text
+ int inlen, // size of encrypted text
+ BYTE *output, // plain text
+ int *outlen, // OUTPUT: size of plaintext
+ int checksumlen // size of checksum; 0=> no checksum
+);
+
+//
+// Wrappers to the RSA code.
+//
+
+int __cdecl
+RSAGetInputBlockSize(
+ BYTE *keydata,
+ int keylen
+);
+
+BYTE * __cdecl
+RSAGetModulus(
+ BYTE *keydata,
+ int keylen,
+ int *modSize
+);
+
+BYTE * _cdecl
+RSAGetPublicExponent(
+ BYTE *keydata,
+ int keylen,
+ int *expSize
+);
+
+int __cdecl
+RSAPack(
+ BYTE *input,
+ int inlen,
+ BYTE *output,
+ int blocksize
+);
+
+int __cdecl
+RSAPublic(
+ BYTE *pukeydata, // BSAFE 1 itemized public key data
+ int pukeylen, // length of BSAFE1 keydata (including sign)
+ BYTE *input, // input block
+ int inlen, // size of input (< modulus)
+ BYTE *output // encrypted block (modulus sized)
+);
+
+int __cdecl
+RSAPrivate(
+ BYTE *prkeydata,
+ int prkeylen,
+ BYTE *input,
+ int inlen,
+ BYTE *output
+);
+
+int __cdecl
+RSAModMpy(
+ BYTE *pukeydata, // BSAFE 1 itemized public key data
+ int pukeylen, // length of BSAFE1 keydata (including sign)
+ BYTE *input1, // input block
+ int inlen1, // size of input (< modulus)
+ BYTE *input2, // multiplier
+ int inlen2, // size of multiplier
+ BYTE *output // encrypted block (modulus sized)
+);
+
+int __cdecl
+RSAModExp(
+ BYTE *pukeydata, // BSAFE 1 itemized public key data
+ int pukeylen, // length of BSAFE1 keydata (including sign)
+ BYTE *input1, // input block
+ int inlen1, // size of input (< modulus)
+ BYTE *exponent,
+ int explen,
+ BYTE *output // encrypted block (modulus sized)
+);
+
diff --git a/private/nw/rdr/data.c b/private/nw/rdr/data.c
new file mode 100644
index 000000000..4a0352afe
--- /dev/null
+++ b/private/nw/rdr/data.c
@@ -0,0 +1,373 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ NwData.c
+
+Abstract:
+
+ This module declares the global data used by the Nw file system.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h>
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
+
+PEPROCESS FspProcess;
+
+PDEVICE_OBJECT FileSystemDeviceObject = NULL;
+
+//
+// The volume control block for the redirector device.
+//
+
+RCB NwRcb;
+
+//
+// The ScbSpinLock protects the entire ScbQueue and the first part of the
+// Scb entries on the queue. The first part of the Scb includes the name
+// of the server and a reference count
+//
+
+KSPIN_LOCK ScbSpinLock;
+LIST_ENTRY ScbQueue;
+
+//
+// A permanent SCB to synchronize access to the network.
+//
+
+NONPAGED_SCB NwPermanentNpScb;
+
+LARGE_INTEGER NwMaxLarge = {MAXULONG,MAXLONG};
+ULONG NwAbsoluteTotalWaitTime = 0;
+
+TDI_ADDRESS_IPX OurAddress = {0,0,0,0,0,0,0,0};
+UNICODE_STRING IpxTransportName;
+HANDLE IpxHandle = NULL;
+PDEVICE_OBJECT pIpxDeviceObject = NULL;
+PFILE_OBJECT pIpxFileObject = NULL;
+
+LIST_ENTRY LogonList;
+LOGON Guest;
+LARGE_INTEGER DefaultLuid = SYSTEM_LUID;
+
+//
+// A global list of VCBs, and a monotonic increasing VCB entry, used to
+// control connection enumeration.
+//
+
+LIST_ENTRY GlobalVcbList;
+ULONG CurrentVcbEntry;
+
+#if 0
+//
+// HACKHACK - List of outstanding find notify request
+// Protected by NwRcb resource.
+//
+
+LIST_ENTRY FnList;
+#endif
+
+//
+// Drive mapping table of redirected drives. 26 disk drive mappings +
+// 10 LPT mappings.
+//
+// Netware supports 32 disk redirections, but this funkiness is handled
+// by the 16-bit code.
+//
+
+PVCB DriveMapTable[DRIVE_MAP_TABLE_SIZE];
+
+FAST_IO_DISPATCH NwFastIoDispatch;
+
+//
+// Scavenger related data
+//
+
+ULONG NwScavengerTickCount; // The current tick count
+ULONG NwScavengerTickRunCount; // The count at which to run the scavenger routine
+KSPIN_LOCK NwScavengerSpinLock; // Lock to protect access to the above.
+
+//
+// Message queue data
+//
+
+LIST_ENTRY NwGetMessageList; // List of Get Message IRP contexts
+KSPIN_LOCK NwMessageSpinLock; // Protects the list above.
+
+//
+// Pending lock list
+//
+
+LIST_ENTRY NwPendingLockList; // List of pending File lock IRP contexts
+KSPIN_LOCK NwPendingLockSpinLock;// Protects the list above.
+
+//
+// Lock to synchronize all file opens.
+//
+
+ERESOURCE NwOpenResource;
+
+//
+// Configuration data
+//
+
+BOOLEAN NwBurstModeEnabled = FALSE;
+ULONG NwMaxSendSize = 0;
+ULONG NwMaxReceiveSize = 0;
+ULONG NwPrintOptions = 0x98; // BUGBUG
+UNICODE_STRING NwProviderName = { 0, 0, NULL };
+
+LONG MaxSendDelay = 50000;
+LONG MaxReceiveDelay = 50000;
+LONG MinSendDelay = 0;
+LONG MinReceiveDelay = 0;
+LONG BurstSuccessCount = 1;
+LONG BurstSuccessCount2 = 3;
+LONG AllowGrowth = 0;
+LONG DontShrink = 0;
+LONG SendExtraNcp = 1;
+LONG DefaultMaxPacketSize = 0;
+LONG PacketThreshold = 1500; // Size to use Large vs Small PacketAdjustment
+LONG LargePacketAdjustment = 38;
+LONG LipPacketAdjustment = 0;
+LONG LipAccuracy = BURST_PACKET_SIZE_TOLERANCE;
+LONG Japan = 0; // Controls special DBCS translation
+LONG DisableReadCache = 0; // disable file i/o read cache
+LONG DisableWriteCache = 0; // disable file i/o write cache
+LONG FavourLongNames = 0 ; // use LFN where possible
+LARGE_INTEGER TimeOutEventInterval = {0, 0};
+LONG MaxWriteTimeout = 50 ; // tick counts (see write.c)
+LONG MaxReadTimeout = 50 ; // tick counts (see read.c)
+LONG WriteTimeoutMultiplier = 100; // expressed as percentage (see write.c)
+LONG ReadTimeoutMultiplier = 100; // expressed as percentage (see read.c)
+
+ULONG EnableMultipleConnects = 0;
+
+ULONG ReadExecOnlyFiles = 0;
+
+//
+// Static storage area for perfmon statistics
+//
+
+NW_REDIR_STATISTICS Stats;
+ULONG ContextCount = 0;
+
+//
+// Data structure used to track discardable code.
+//
+
+SECTION_DESCRIPTOR NwSectionDescriptor;
+ERESOURCE NwUnlockableCodeResource;
+
+//
+// The lock timeout value.
+//
+
+ULONG LockTimeoutThreshold = 1;
+
+#ifdef _PNP_POWER
+
+//
+// The TDI PNP Bind handle.
+//
+
+HANDLE TdiBindingHandle = NULL;
+UNICODE_STRING TdiIpxDeviceName;
+WCHAR IpxDevice[] = L"\\Device\\NwlnkIpx";
+
+#endif
+
+//
+// We can't have the scavenger and a line change request running
+// at the same time since they both run on worker threads and
+// walk across all the SCBs. Therefore, when either is running,
+// we set the WorkerRunning value used by the scavenger to TRUE.
+// If a scavenger run tries to happen while a line change request
+// is running, it gets skipped. If a line change request comes in
+// while the scavenger is running, we set DelayedProcessLineChange
+// to TRUE and run it when the scavenger finishes.
+//
+// These values are protected by the existing scavenger spin lock.
+//
+
+BOOLEAN DelayedProcessLineChange = FALSE;
+PIRP DelayedLineChangeIrp = NULL;
+
+#ifdef NWDBG
+
+ULONG NwDebug = 0;
+//ULONG NwDebug = 0xffffff;
+ULONG NwMemDebug = 0xffffffff;
+LONG NwDebugTraceIndent = 0;
+
+ULONG NwFsdEntryCount = 0;
+ULONG NwFspEntryCount = 0;
+ULONG NwIoCallDriverCount = 0;
+
+LONG NwPerformanceTimerLevel = 0x00000000;
+
+ULONG NwTotalTicks[32] = { 0 };
+
+//
+// Debug data for tracking pool usage
+//
+
+KSPIN_LOCK NwDebugInterlock;
+ERESOURCE NwDebugResource;
+
+LIST_ENTRY NwPagedPoolList;
+LIST_ENTRY NwNonpagedPoolList;
+
+ULONG MdlCount;
+ULONG IrpCount;
+
+#endif // NWDBG
+
+//
+// Configurable parameters.
+//
+
+SHORT DefaultRetryCount = DEFAULT_RETRY_COUNT;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwInitializeData )
+#endif
+
+VOID
+NwInitializeData(
+ VOID
+ )
+{
+ LARGE_INTEGER Now;
+
+ PAGED_CODE();
+
+ NwRcb.State = RCB_STATE_STOPPED;
+
+#ifdef NWDBG
+ // Initialize pool before allocating any memory
+ InitializeListHead( &NwPagedPoolList );
+ InitializeListHead( &NwNonpagedPoolList );
+ ExInitializeResource( &NwDebugResource );
+ KeInitializeSpinLock( &NwDebugInterlock );
+
+ MdlCount = 0;
+ IrpCount = 0;
+#endif
+
+ ExInitializeResource( &NwOpenResource );
+
+ //
+ // Initialize the scavenger spin lock and run tick count.
+ //
+
+ KeInitializeSpinLock( &NwScavengerSpinLock );
+ NwScavengerTickRunCount = DEFAULT_SCAVENGER_TICK_RUN_COUNT;
+
+ RtlInitUnicodeString( &IpxTransportName, NULL );
+
+#ifdef _PNP_POWER
+
+ RtlInitUnicodeString( &TdiIpxDeviceName, IpxDevice );
+
+#endif
+
+ //
+ // Allocate a permanent Non-paged SCB. This SCB is used to
+ // synchronize access to finding the nearest server.
+ // This initialization must be done before the first possible call
+ // to UnloadDriver.
+ //
+
+ RtlZeroMemory( &NwPermanentNpScb, sizeof( NONPAGED_SCB ) );
+
+ NwPermanentNpScb.NodeTypeCode = NW_NTC_SCBNP;
+ NwPermanentNpScb.NodeByteSize = sizeof(NONPAGED_SCB);
+ NwPermanentNpScb.Reference = 1;
+
+ InitializeListHead( &NwPermanentNpScb.Requests );
+
+ //
+ // Initialize the logonlist to have a default entry with server NULL,
+ // username "GUEST" and null password. This will always be the last
+ // entry on the logonlist so that the workstation service can supply
+ // an override.
+ //
+
+ InitializeListHead( &LogonList );
+
+ Guest.NodeTypeCode = NW_NTC_LOGON;
+ Guest.NodeByteSize = sizeof(LOGON);
+ RtlInitUnicodeString( &Guest.ServerName, NULL );
+ RtlInitUnicodeString( &Guest.PassWord, NULL );
+ RtlInitUnicodeString( &Guest.UserName, L"GUEST" );
+ Guest.UserUid = DefaultLuid;
+ InitializeListHead( &Guest.NdsCredentialList );
+ InsertTailList( &LogonList, &Guest.Next );
+
+ //
+ // Initialize the global VCB list.
+ //
+
+ InitializeListHead( &GlobalVcbList );
+ CurrentVcbEntry = 1;
+
+ //
+ // Initialize the Get message queue.
+ //
+
+ InitializeListHead( &NwGetMessageList );
+ KeInitializeSpinLock( &NwMessageSpinLock );
+
+ //
+ // Initialize the Pending lock queue.
+ //
+
+ InitializeListHead( &NwPendingLockList );
+ KeInitializeSpinLock( &NwPendingLockSpinLock );
+
+ //
+ // Insert the Permanent SCB in the global list of SCBs.
+ //
+
+ InsertHeadList( &ScbQueue, &NwPermanentNpScb.ScbLinks );
+
+#if 0
+ // HACKHACK
+ InitializeListHead( &FnList );
+#endif
+
+ //
+ // Seed the random number generator.
+ //
+
+ KeQuerySystemTime( &Now );
+ srand( Now.LowPart );
+
+ RtlZeroMemory( &Stats, sizeof( NW_REDIR_STATISTICS ) );
+
+ ExInitializeResource( &NwUnlockableCodeResource );
+
+ NwSectionDescriptor.Base = BurstReadTimeout;
+ NwSectionDescriptor.Handle = 0;
+ NwSectionDescriptor.ReferenceCount = 0;
+
+ return;
+}
+
+
diff --git a/private/nw/rdr/data.h b/private/nw/rdr/data.h
new file mode 100644
index 000000000..d7d9f3cea
--- /dev/null
+++ b/private/nw/rdr/data.h
@@ -0,0 +1,238 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Data.h
+
+Abstract:
+
+ This module declares the global data used by the NetWare redirector
+ file system.
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NWDATA_
+#define _NWDATA_
+
+extern PEPROCESS FspProcess;
+extern PDEVICE_OBJECT FileSystemDeviceObject;
+extern RCB NwRcb;
+
+extern KSPIN_LOCK ScbSpinLock;
+extern LIST_ENTRY ScbQueue;
+extern NONPAGED_SCB NwPermanentNpScb;
+extern SCB NwPermanentScb;
+
+extern LARGE_INTEGER NwMaxLarge;
+extern ULONG NwAbsoluteTotalWaitTime;
+
+extern TDI_ADDRESS_IPX OurAddress;
+extern UNICODE_STRING IpxTransportName;
+extern HANDLE IpxHandle;
+extern PDEVICE_OBJECT pIpxDeviceObject;
+extern PFILE_OBJECT pIpxFileObject;
+
+extern LIST_ENTRY LogonList;
+extern LOGON Guest;
+extern LARGE_INTEGER DefaultLuid;
+
+extern LIST_ENTRY GlobalVcbList;
+extern ULONG CurrentVcbEntry;
+
+//
+// Drive mapping table of redirected drives.
+//
+
+extern PVCB DriveMapTable[];
+
+//
+// The global structure used to contain our fast I/O callbacks
+//
+
+extern FAST_IO_DISPATCH NwFastIoDispatch;
+
+//
+// Configurable paramaters
+//
+
+extern SHORT DefaultRetryCount;
+
+extern ULONG NwScavengerTickCount;
+extern ULONG NwScavengerTickRunCount;
+extern KSPIN_LOCK NwScavengerSpinLock;
+
+extern LIST_ENTRY NwGetMessageList;
+extern KSPIN_LOCK NwMessageSpinLock;
+
+extern LIST_ENTRY NwPendingLockList;
+extern KSPIN_LOCK NwPendingLockSpinLock;
+
+extern ERESOURCE NwOpenResource;
+
+#if 0
+extern LIST_ENTRY FnList; // HACKHACK
+#endif
+
+extern BOOLEAN NwBurstModeEnabled;
+extern ULONG NwMaxSendSize;
+extern ULONG NwMaxReceiveSize;
+extern ULONG NwPrintOptions;
+extern UNICODE_STRING NwProviderName;
+
+extern LONG MaxSendDelay;
+extern LONG MaxReceiveDelay;
+extern LONG MinSendDelay;
+extern LONG MinReceiveDelay;
+extern LONG BurstSuccessCount;
+extern LONG BurstSuccessCount2;
+extern LONG AllowGrowth;
+extern LONG DontShrink;
+extern LONG SendExtraNcp;
+extern LONG DefaultMaxPacketSize;
+extern LONG PacketThreshold;
+extern LONG LargePacketAdjustment;
+extern LONG LipPacketAdjustment;
+extern LONG LipAccuracy;
+extern LONG MaxWriteTimeout;
+extern LONG MaxReadTimeout;
+extern LONG WriteTimeoutMultiplier;
+extern LONG ReadTimeoutMultiplier;
+
+extern ULONG EnableMultipleConnects;
+
+extern ULONG ReadExecOnlyFiles;
+
+extern LONG Japan; // Controls special DBCS translation
+extern LONG DisableReadCache ;
+extern LONG DisableWriteCache ;
+extern LONG FavourLongNames ; // use LFN where possible
+
+extern LARGE_INTEGER TimeOutEventInterval;
+
+extern NW_REDIR_STATISTICS Stats;
+extern ULONG ContextCount;
+
+extern SECTION_DESCRIPTOR NwSectionDescriptor;
+extern ERESOURCE NwUnlockableCodeResource;
+
+extern ULONG LockTimeoutThreshold;
+
+#ifdef _PNP_POWER
+
+extern HANDLE TdiBindingHandle;
+extern UNICODE_STRING TdiIpxDeviceName;
+
+#endif
+
+extern BOOLEAN DelayedProcessLineChange;
+extern PIRP DelayedLineChangeIrp;
+
+#ifdef NWDBG
+
+#define DEBUG_TRACE_ALWAYS (0x00000000)
+#define DEBUG_TRACE_CLEANUP (0x00000001)
+#define DEBUG_TRACE_CLOSE (0x00000002)
+#define DEBUG_TRACE_CREATE (0x00000004)
+#define DEBUG_TRACE_FSCTRL (0x00000008)
+#define DEBUG_TRACE_IPX (0x00000010)
+#define DEBUG_TRACE_LOAD (0x00000020)
+#define DEBUG_TRACE_EXCHANGE (0x00000040)
+#define DEBUG_TRACE_FILOBSUP (0x00000080)
+#define DEBUG_TRACE_STRUCSUP (0x00000100)
+#define DEBUG_TRACE_FSP_DISPATCHER (0x00000200)
+#define DEBUG_TRACE_FSP_DUMP (0x00000400)
+#define DEBUG_TRACE_WORKQUE (0x00000800)
+#define DEBUG_TRACE_UNWIND (0x00001000)
+#define DEBUG_TRACE_CATCH_EXCEPTIONS (0x00002000)
+#define DEBUG_TRACE_ICBS (0x00004000)
+#define DEBUG_TRACE_FILEINFO (0x00008000)
+#define DEBUG_TRACE_DIRCTRL (0x00010000)
+#define DEBUG_TRACE_CONVERT (0x00020000)
+#define DEBUG_TRACE_WRITE (0x00040000)
+#define DEBUG_TRACE_READ (0x00080000)
+#define DEBUG_TRACE_VOLINFO (0x00100000)
+#define DEBUG_TRACE_LOCKCTRL (0x00200000)
+#define DEBUG_TRACE_USERNCP (0x00400000)
+#define DEBUG_TRACE_SECURITY (0x00800000)
+#define DEBUG_TRACE_CACHE (0x01000000)
+#define DEBUG_TRACE_LIP (0x02000000)
+#define DEBUG_TRACE_MDL (0x04000000)
+
+#define DEBUG_TRACE_NDS (0x10000000)
+#define DEBUG_TRACE_SCAVENGER (0x40000000)
+#define DEBUG_TRACE_TIMER (0x80000000)
+
+extern ULONG NwDebug;
+extern ULONG NwMemDebug;
+extern LONG NwDebugTraceIndent;
+
+#define DebugTrace( I, L, M, P ) RealDebugTrace( I, L, "%08lx: %*s"M, (PVOID)(P) )
+
+#define DebugUnwind(X) { \
+ if (AbnormalTermination()) { \
+ DebugTrace(0, DEBUG_TRACE_UNWIND, #X ", Abnormal termination.\n", 0); \
+ } \
+}
+
+//
+// The following variables are used to keep track of the total amount
+// of requests processed by the file system, and the number of requests
+// that end up being processed by the Fsp thread. The first variable
+// is incremented whenever an Irp context is created (which is always
+// at the start of an Fsd entry point) and the second is incremented
+// by read request.
+//
+
+extern ULONG NwFsdEntryCount;
+extern ULONG NwFspEntryCount;
+extern ULONG NwIoCallDriverCount;
+extern ULONG NwTotalTicks[];
+
+extern KSPIN_LOCK NwDebugInterlock;
+extern ERESOURCE NwDebugResource;
+
+extern LIST_ENTRY NwPagedPoolList;
+extern LIST_ENTRY NwNonpagedPoolList;
+
+extern ULONG MdlCount;
+extern ULONG IrpCount;
+
+#define DebugDoit(X) {X;}
+
+extern LONG NwPerformanceTimerLevel;
+
+#define TimerStart(LEVEL) { \
+ LARGE_INTEGER TStart, TEnd; \
+ LARGE_INTEGER TElapsed; \
+ TStart = KeQueryPerformanceCounter( NULL ); \
+
+#define TimerStop(LEVEL,s) \
+ TEnd = KeQueryPerformanceCounter( NULL ); \
+ TElapsed = RtlLargeIntegerSubtract( TEnd, TStart ); \
+ /* NwTotalTicks[NwLogOf(LEVEL)] += TElapsed.LowPart; */ \
+ if (FlagOn( NwPerformanceTimerLevel, (LEVEL))) { \
+ DbgPrint("Time of %s %ld\n", (s), TElapsed.LowPart ); \
+ } \
+}
+
+#else
+
+#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;}
+#define DebugUnwind(X) {NOTHING;}
+#define DebugDoit(X) {NOTHING;}
+
+#define TimerStart(LEVEL)
+#define TimerStop(LEVEL,s)
+
+#endif // NWDBG
+
+#endif // _NWDATA_
+
diff --git a/private/nw/rdr/debug.c b/private/nw/rdr/debug.c
new file mode 100644
index 000000000..52e356300
--- /dev/null
+++ b/private/nw/rdr/debug.c
@@ -0,0 +1,780 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Debug.c
+
+Abstract:
+
+ This module declares the Debug only code used by the NetWare redirector
+ file system.
+
+Author:
+
+ Colin Watson [ColinW] 05-Jan-1993
+
+Revision History:
+
+--*/
+#include "procs.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+#define LINE_SIZE 511
+#define BUFFER_LINES 50
+
+
+#ifdef NWDBG
+
+#include <stdlib.h> // rand()
+int FailAllocateMdl = 0;
+
+ULONG MaxDump = 256;
+CHAR DBuffer[BUFFER_LINES*LINE_SIZE+1];
+PCHAR DBufferPtr = DBuffer;
+
+//
+// The reference count debug buffer.
+//
+
+CHAR RBuffer[BUFFER_LINES*LINE_SIZE+1];
+PCHAR RBufferPtr = RBuffer;
+
+LIST_ENTRY MdlList;
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+ULONG
+NwMemDbg (
+ IN PCH Format,
+ ...
+ )
+
+//++
+//
+// Routine Description:
+//
+// Effectively DbgPrint to the debugging console.
+//
+// Arguments:
+//
+// Same as for DbgPrint
+//
+//--
+
+{
+ va_list arglist;
+ int Length;
+
+ //
+ // Format the output into a buffer and then print it.
+ //
+
+ va_start(arglist, Format);
+
+ Length = _vsnprintf(DBufferPtr, LINE_SIZE, Format, arglist);
+
+ if (Length < 0) {
+ DbgPrint( "NwRdr: Message is too long for NwMemDbg\n");
+ return 0;
+ }
+
+ va_end(arglist);
+
+ ASSERT( Length <= LINE_SIZE );
+ ASSERT( Length != 0 );
+ ASSERT( DBufferPtr < &DBuffer[BUFFER_LINES*LINE_SIZE+1]);
+ ASSERT( DBufferPtr >= DBuffer);
+
+ DBufferPtr += Length;
+ DBufferPtr[0] = '\0';
+
+ // Avoid running off the end of the buffer and exit
+
+ if (DBufferPtr >= (DBuffer+((BUFFER_LINES-1) * LINE_SIZE))) {
+ DBufferPtr = DBuffer;
+
+ }
+
+ return 0;
+}
+
+VOID
+RefDbgTrace (
+ PVOID Resource,
+ DWORD Count,
+ BOOLEAN Reference,
+ PBYTE FileName,
+ UINT Line
+)
+/**
+
+ Routine Description:
+
+ NwRefDebug logs reference count operations to expose
+ reference count errors or leaks in the redirector.
+
+ Arguments:
+
+ Resource - The object we're adjusting the reference count on.
+ Count - The current count on the object.
+ Reference - If TRUE we are doing a REFERENCE.
+ Otherwise, we are doing a DEREFERENCE.
+ FileName - The callers file name.
+ Line - The callers line number.
+
+**/
+{
+ int Length;
+ int NextCount;
+
+ //
+ // Format the output into a buffer and then print it.
+ //
+
+ if ( Reference )
+ NextCount = Count + 1;
+ else
+ NextCount = Count - 1;
+
+ Length = sprintf( RBufferPtr,
+ "%08lx: R=%08lx, %lu -> %lu (%s, line %d)\n",
+ (PVOID)PsGetCurrentThread(),
+ Resource,
+ Count,
+ NextCount,
+ FileName,
+ Line );
+
+ if (Length < 0) {
+ DbgPrint( "NwRdr: Message is too long for NwRefDbg\n");
+ return;
+ }
+
+ ASSERT( Length <= LINE_SIZE );
+ ASSERT( Length != 0 );
+ ASSERT( RBufferPtr < &RBuffer[BUFFER_LINES*LINE_SIZE+1]);
+ ASSERT( RBufferPtr >= RBuffer);
+
+ RBufferPtr += Length;
+ RBufferPtr[0] = '\0';
+
+ // Avoid running off the end of the buffer and exit
+
+ if (RBufferPtr >= (RBuffer+((BUFFER_LINES-1) * LINE_SIZE))) {
+ RBufferPtr = RBuffer;
+ }
+
+ return;
+}
+
+VOID
+RealDebugTrace(
+ LONG Indent,
+ ULONG Level,
+ PCH Message,
+ PVOID Parameter
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if ( (Level == 0) || (NwMemDebug & Level )) {
+ NwMemDbg( Message, PsGetCurrentThread(), 1, "", Parameter );
+ }
+
+ if ( (Level == 0) || (NwDebug & Level )) {
+
+ if ( Indent < 0) {
+ NwDebugTraceIndent += Indent;
+ }
+
+ DbgPrint( Message, PsGetCurrentThread(), NwDebugTraceIndent, "", Parameter );
+
+ if ( Indent > 0) {
+ NwDebugTraceIndent += Indent;
+ }
+
+ if (NwDebugTraceIndent < 0) {
+ NwDebugTraceIndent = 0;
+ }
+ }
+}
+
+VOID
+dump(
+ IN ULONG Level,
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style if debug
+ output is turned on for this level.
+
+Arguments:
+
+ IN Level - 0 if always display. Otherwise only display if a
+ corresponding bit is set in NwDebug.
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+
+ if ( (Level == 0) || (NwDebug & Level )) {
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 16 ? len : 16;
+
+ DbgPrint("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ DbgPrint("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+ NwMemDbg ( "%lx: %s%.*s%s\n",
+ far_pchar, s, 1 + ((16 - l) * 3), "", t);
+
+ len -= l;
+ far_pchar += l;
+ }
+ DbgPrint("\n");
+
+ }
+}
+
+VOID
+dumpMdl(
+ IN ULONG Level,
+ IN PMDL Mdl
+ )
+/*++
+
+Routine Description:
+ Dump the memory described by each part of a chained Mdl.
+
+Arguments:
+
+ IN Level - 0 if always display. Otherwise only display if a
+ corresponding bit is set in NwDebug.
+
+ Mdl - Supplies the addresses of the memory to be dumped.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PMDL Next;
+ ULONG len;
+
+
+ if ( (Level == 0) || (NwDebug & Level )) {
+ Next = Mdl; len = 0;
+ do {
+
+ dump(Level, MmGetSystemAddressForMdl(Next), MIN(MmGetMdlByteCount(Next), MaxDump-len));
+
+ len += MmGetMdlByteCount(Next);
+ } while ( (Next = Next->Next) != NULL &&
+ len <= MaxDump);
+ }
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ 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;
+
+ flag;
+}
+
+typedef struct _NW_POOL_HEADER {
+ ULONG Signature;
+ ULONG BufferSize;
+ ULONG BufferType;
+ LIST_ENTRY ListEntry;
+ ULONG Pad; // Pad to Q-word align
+} NW_POOL_HEADER, *PNW_POOL_HEADER;
+
+typedef struct _NW_POOL_TRAILER {
+ ULONG Signature;
+} NW_POOL_TRAILER;
+
+typedef NW_POOL_TRAILER UNALIGNED *PNW_POOL_TRAILER;
+
+PVOID
+NwAllocatePool(
+ ULONG Type,
+ ULONG Size,
+ BOOLEAN RaiseStatus
+ )
+{
+ PCHAR Buffer;
+ PNW_POOL_HEADER PoolHeader;
+ PNW_POOL_TRAILER PoolTrailer;
+
+ if ( RaiseStatus ) {
+ Buffer = FsRtlAllocatePool(
+ Type,
+ sizeof( NW_POOL_HEADER ) + sizeof( NW_POOL_TRAILER ) + Size );
+ } else {
+#ifndef QFE_BUILD
+ Buffer = ExAllocatePoolWithTag(
+ Type,
+ sizeof( NW_POOL_HEADER )+sizeof( NW_POOL_TRAILER )+Size,
+ 'scwn' );
+#else
+ Buffer = ExAllocatePool(
+ Type,
+ sizeof( NW_POOL_HEADER )+sizeof( NW_POOL_TRAILER )+Size );
+#endif
+
+ if ( Buffer == NULL ) {
+ return( NULL );
+ }
+ }
+
+ PoolHeader = (PNW_POOL_HEADER)Buffer;
+ PoolTrailer = (PNW_POOL_TRAILER)(Buffer + sizeof( NW_POOL_HEADER ) + Size);
+
+ PoolHeader->Signature = 0x11111111;
+ PoolHeader->BufferSize = Size;
+ PoolHeader->BufferType = Type;
+
+ PoolTrailer->Signature = 0x99999999;
+
+ if ( Type == PagedPool ) {
+ ExAcquireResourceExclusive( &NwDebugResource, TRUE );
+ InsertTailList( &NwPagedPoolList, &PoolHeader->ListEntry );
+ ExReleaseResource( &NwDebugResource );
+ } else if ( Type == NonPagedPool ) {
+ ExInterlockedInsertTailList( &NwNonpagedPoolList, &PoolHeader->ListEntry, &NwDebugInterlock );
+ } else {
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+ return( Buffer + sizeof( NW_POOL_HEADER ) );
+}
+
+VOID
+NwFreePool(
+ PVOID Buffer
+ )
+{
+ PNW_POOL_HEADER PoolHeader;
+ PNW_POOL_TRAILER PoolTrailer;
+ KIRQL OldIrql;
+
+ PoolHeader = (PNW_POOL_HEADER)((PCHAR)Buffer - sizeof( NW_POOL_HEADER ));
+ ASSERT( PoolHeader->Signature == 0x11111111 );
+ ASSERT( PoolHeader->BufferType == PagedPool ||
+ PoolHeader->BufferType == NonPagedPool );
+
+ PoolTrailer = (PNW_POOL_TRAILER)((PCHAR)Buffer + PoolHeader->BufferSize );
+ ASSERT( PoolTrailer->Signature == 0x99999999 );
+
+ if ( PoolHeader->BufferType == PagedPool ) {
+ ExAcquireResourceExclusive( &NwDebugResource, TRUE );
+ RemoveEntryList( &PoolHeader->ListEntry );
+ ExReleaseResource( &NwDebugResource );
+ } else {
+ KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
+ RemoveEntryList( &PoolHeader->ListEntry );
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+ }
+
+ ExFreePool( PoolHeader );
+}
+
+//
+// Debug functions for allocating and deallocating IRPs and MDLs
+//
+
+PIRP
+NwAllocateIrp(
+ CCHAR Size,
+ BOOLEAN ChargeQuota
+ )
+{
+ ExInterlockedIncrementLong( &IrpCount, &NwDebugInterlock );
+ return IoAllocateIrp( Size, ChargeQuota );
+}
+
+VOID
+NwFreeIrp(
+ PIRP Irp
+ )
+{
+ ExInterlockedDecrementLong( &IrpCount, &NwDebugInterlock );
+ IoFreeIrp( Irp );
+}
+
+typedef struct _NW_MDL {
+ LIST_ENTRY Next;
+ PUCHAR File;
+ int Line;
+ PMDL pMdl;
+} NW_MDL, *PNW_MDL;
+
+//int DebugLine = 2461;
+
+PMDL
+NwAllocateMdl(
+ PVOID Va,
+ ULONG Length,
+ BOOLEAN Secondary,
+ BOOLEAN ChargeQuota,
+ PIRP Irp,
+ PUCHAR FileName,
+ int Line
+ )
+{
+ PNW_MDL Buffer;
+
+ static BOOLEAN MdlSetup = FALSE;
+
+ if (MdlSetup == FALSE) {
+
+ InitializeListHead( &MdlList );
+
+ MdlSetup = TRUE;
+ }
+
+ if ( FailAllocateMdl != 0 ) {
+ if ( ( rand() % FailAllocateMdl ) == 0 ) {
+ return(NULL);
+ }
+ }
+
+#ifndef QFE_BUILD
+ Buffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof( NW_MDL),
+ 'scwn' );
+#else
+ Buffer = ExAllocatePool(
+ NonPagedPool,
+ sizeof( NW_MDL));
+#endif
+
+ if ( Buffer == NULL ) {
+ return( NULL );
+ }
+
+ ExInterlockedIncrementLong( &MdlCount, &NwDebugInterlock );
+
+ Buffer->File = FileName;
+ Buffer->Line = Line;
+ Buffer->pMdl = IoAllocateMdl( Va, Length, Secondary, ChargeQuota, Irp );
+
+ ExInterlockedInsertTailList( &MdlList, &Buffer->Next, &NwDebugInterlock );
+
+/*
+ if (DebugLine == Line) {
+ DebugTrace( 0, DEBUG_TRACE_MDL, "AllocateMdl -> %08lx\n", Buffer->pMdl );
+ DebugTrace( 0, DEBUG_TRACE_MDL, "AllocateMdl -> %08lx\n", Line );
+ }
+*/
+ return(Buffer->pMdl);
+}
+
+VOID
+NwFreeMdl(
+ PMDL Mdl
+ )
+{
+ PLIST_ENTRY MdlEntry;
+ PNW_MDL Buffer;
+ KIRQL OldIrql;
+
+ ExInterlockedDecrementLong( &MdlCount, &NwDebugInterlock );
+
+ KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
+ // Find the Mdl in the list and remove it.
+
+ for (MdlEntry = MdlList.Flink ;
+ MdlEntry != &MdlList ;
+ MdlEntry = MdlEntry->Flink ) {
+
+ Buffer = CONTAINING_RECORD( MdlEntry, NW_MDL, Next );
+
+ if (Buffer->pMdl == Mdl) {
+
+ RemoveEntryList( &Buffer->Next );
+
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+
+ IoFreeMdl( Mdl );
+ DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMDL - %08lx\n", Mdl );
+/*
+ if (DebugLine == Buffer->Line) {
+ DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMdl -> %08lx\n", Mdl );
+ DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMdl -> %08lx\n", Buffer->Line );
+ }
+*/
+ ExFreePool(Buffer);
+
+ return;
+ }
+ }
+ ASSERT( FALSE );
+
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+}
+
+/*
+VOID
+NwLookForMdl(
+ )
+{
+ PLIST_ENTRY MdlEntry;
+ PNW_MDL Buffer;
+ KIRQL OldIrql;
+
+ KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
+ // Find the Mdl in the list and remove it.
+
+ for (MdlEntry = MdlList.Flink ;
+ MdlEntry != &MdlList ;
+ MdlEntry = MdlEntry->Flink ) {
+
+ Buffer = CONTAINING_RECORD( MdlEntry, NW_MDL, Next );
+
+ if (Buffer->Line == DebugLine) {
+
+ DebugTrace( 0, DEBUG_TRACE_MDL, "LookForMdl -> %08lx\n", Buffer );
+ DbgBreakPoint();
+
+ }
+ }
+
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+}
+*/
+
+//
+// Function version of resource macro, to make debugging easier.
+//
+
+VOID
+NwAcquireExclusiveRcb(
+ PRCB Rcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceExclusive( &((Rcb)->Resource), Wait );
+}
+
+VOID
+NwAcquireSharedRcb(
+ PRCB Rcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceShared( &((Rcb)->Resource), Wait );
+}
+
+VOID
+NwReleaseRcb(
+ PRCB Rcb )
+{
+ ExReleaseResource( &((Rcb)->Resource) );
+}
+
+VOID
+NwAcquireExclusiveFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceExclusive( &((pFcb)->Resource), Wait );
+}
+
+VOID
+NwAcquireSharedFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceShared( &((pFcb)->Resource), Wait );
+}
+
+VOID
+NwReleaseFcb(
+ PNONPAGED_FCB pFcb )
+{
+ ExReleaseResource( &((pFcb)->Resource) );
+}
+
+VOID
+NwAcquireOpenLock(
+ VOID
+ )
+{
+ ExAcquireResourceExclusive( &NwOpenResource, TRUE );
+}
+
+VOID
+NwReleaseOpenLock(
+ VOID
+ )
+{
+ ExReleaseResource( &NwOpenResource );
+}
+
+
+//
+// code to dump ICBs
+//
+
+VOID DumpIcbs(VOID)
+{
+ PVCB Vcb;
+ PFCB Fcb;
+ PICB Icb;
+ PLIST_ENTRY VcbListEntry;
+ PLIST_ENTRY FcbListEntry;
+ PLIST_ENTRY IcbListEntry;
+ KIRQL OldIrql;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ DbgPrint("\nICB Pid State Scb/Fcb Name\n", 0);
+ for ( VcbListEntry = GlobalVcbList.Flink;
+ VcbListEntry != &GlobalVcbList ;
+ VcbListEntry = VcbListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( VcbListEntry, VCB, GlobalVcbListEntry );
+
+ for ( FcbListEntry = Vcb->FcbList.Flink;
+ FcbListEntry != &(Vcb->FcbList) ;
+ FcbListEntry = FcbListEntry->Flink ) {
+
+ Fcb = CONTAINING_RECORD( FcbListEntry, FCB, FcbListEntry );
+
+ for ( IcbListEntry = Fcb->IcbList.Flink;
+ IcbListEntry != &(Fcb->IcbList) ;
+ IcbListEntry = IcbListEntry->Flink ) {
+
+ Icb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry );
+
+ DbgPrint("%08lx", Icb);
+ DbgPrint(" %08lx",(DWORD)Icb->Pid);
+ DbgPrint(" %08lx",Icb->State);
+ DbgPrint(" %08lx",Icb->SuperType.Scb);
+ DbgPrint(" %wZ\n",
+ &(Icb->FileObject->FileName) );
+ }
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ NwReleaseRcb( &NwRcb );
+}
+
+#endif // ifdef NWDBG
+
+//
+// Ref counting debug routines.
+//
+
+#ifdef NWDBG
+
+VOID
+ChkNwReferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+) {
+
+ if ( (pNpScb)->NodeTypeCode != NW_NTC_SCBNP ) {
+ DbgBreakPoint();
+ }
+
+ if ( !Silent) {
+ RefDbgTrace( pNpScb, pNpScb->Reference, TRUE, FileName, Line );
+ }
+
+ ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock );
+}
+
+VOID
+ChkNwDereferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+) {
+
+ if ( (pNpScb)->Reference == 0 ) {
+ DbgBreakPoint();
+ }
+
+ if ( (pNpScb)->NodeTypeCode != NW_NTC_SCBNP ) {
+ DbgBreakPoint();
+ }
+
+ if ( !Silent ) {
+ RefDbgTrace( pNpScb, pNpScb->Reference, FALSE, FileName, Line );
+ }
+
+ ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock );
+}
+
+#endif
+
diff --git a/private/nw/rdr/deviosup.c b/private/nw/rdr/deviosup.c
new file mode 100644
index 000000000..1ff83c50f
--- /dev/null
+++ b/private/nw/rdr/deviosup.c
@@ -0,0 +1,153 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ deviosup.c
+
+Abstract:
+
+ This module implements the memory locking routines for the netware
+ redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// Local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_DEVIOSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwMapUserBuffer )
+#pragma alloc_text( PAGE, NwLockUserBuffer )
+#endif
+
+
+VOID
+NwMapUserBuffer (
+ IN OUT PIRP Irp,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PVOID *UserBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine obtains a usable virtual address for the user buffer
+ for the current I/O request in the specified mode.
+
+Arguments:
+
+ Irp - Pointer to the Irp for the request.
+
+ AccessMode - UserMode or KernelMode.
+
+ UserBuffer - Returns pointer to mapped user buffer.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ AccessMode;
+
+ //
+ // If there is no Mdl, then we must be in the Fsd, and we can simply
+ // return the UserBuffer field from the Irp.
+ //
+
+ if (Irp->MdlAddress == NULL) {
+
+ *UserBuffer = Irp->UserBuffer;
+ return;
+ }
+
+ //
+ // Get a system virtual address for the buffer.
+ //
+
+ *UserBuffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
+ return;
+}
+
+
+VOID
+NwLockUserBuffer (
+ IN OUT PIRP Irp,
+ IN LOCK_OPERATION Operation,
+ IN ULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine locks the specified buffer for the specified type of
+ access. The file system requires this routine since it does not
+ ask the I/O system to lock its buffers for direct I/O. This routine
+ may only be called from the FSD while still in the user context.
+
+Arguments:
+
+ Irp - Pointer to the IRP for which the buffer is to be locked.
+
+ Operation - IoWriteAccess for read operations, or IoReadAccess for
+ write operations.
+
+ BufferLength - Length of user buffer.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PMDL mdl;
+
+ PAGED_CODE();
+
+ if (Irp->MdlAddress == NULL) {
+
+ //
+ // This read is bound for the current process. Perform the
+ // same functions as above, only do not switch processes.
+ //
+
+ mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, TRUE, Irp );
+
+ if (mdl == NULL) {
+
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ try {
+
+ MmProbeAndLockPages( mdl,
+ Irp->RequestorMode,
+ Operation );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ IoFreeMdl( mdl );
+ Irp->MdlAddress = NULL;
+ ExRaiseStatus( FsRtlNormalizeNtstatus( GetExceptionCode(),
+ STATUS_INVALID_USER_BUFFER ));
+ }
+ }
+}
diff --git a/private/nw/rdr/dir.c b/private/nw/rdr/dir.c
new file mode 100644
index 000000000..64769b9ac
--- /dev/null
+++ b/private/nw/rdr/dir.c
@@ -0,0 +1,1672 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ dir.c
+
+Abstract:
+
+ This module implements the file directory routines for the
+ Netware Redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 4-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+typedef struct _NW_DIRECTORY_INFO {
+ WCHAR FileNameBuffer[NW_MAX_FILENAME_LENGTH];
+ UNICODE_STRING FileName;
+ UCHAR Attributes;
+ USHORT CreationDate;
+ USHORT CreationTime;
+ USHORT LastAccessDate;
+ USHORT LastUpdateDate;
+ USHORT LastUpdateTime;
+ ULONG FileSize;
+ ULONG DosDirectoryEntry;
+} NW_DIRECTORY_INFO, *PNW_DIRECTORY_INFO;
+
+//
+// Local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_DIRCTRL)
+
+NTSTATUS
+NwCommonDirectoryControl (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NwQueryDirectory (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB pIcb
+ );
+
+NTSTATUS
+GetNextFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB Icb,
+ PULONG fileIndexLow,
+ PULONG fileIndexHigh,
+ UCHAR SearchAttributes,
+ PNW_DIRECTORY_INFO NwDirInfo
+ );
+
+NTSTATUS
+NtSearchMaskToNw(
+ IN PUNICODE_STRING UcSearchMask,
+ IN OUT POEM_STRING OemSearchMask,
+ IN PICB Icb,
+ IN BOOLEAN ShortNameSearch
+ );
+
+#if 0
+VOID
+NwCancelFindNotify (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdDirectoryControl )
+#pragma alloc_text( PAGE, NwQueryDirectory )
+#pragma alloc_text( PAGE, GetNextFile )
+#pragma alloc_text( PAGE, NtSearchMaskToNw )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwCommonDirectoryControl )
+#endif
+
+#endif
+
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdDirectoryControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles directory control
+ functions (i.e., query and notify).
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the directory function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdDirectoryControl\n", 0);
+
+ //
+ // Call the common directory control routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonDirectoryControl( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdDirectoryControl -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonDirectoryControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for directory control functions.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PDCB dcb;
+ PVOID fsContext;
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonDirectoryControl...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not an ICB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
+ return status;
+ }
+
+ dcb = (PDCB)icb->SuperType.Fcb;
+ nodeTypeCode = dcb->NodeTypeCode;
+
+ if ( nodeTypeCode != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
+ return status;
+ }
+
+ IrpContext->pScb = icb->SuperType.Fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ //
+ // Acquire exclusive access to the DCB. Get to front of queue
+ // first to avoid deadlock potential.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveFcb( dcb->NonPagedFcb, TRUE );
+
+ try {
+
+ NwVerifyIcb( icb );
+
+ //
+ // We know this is a directory control so we'll case on the
+ // minor function, and call the appropriate work routines.
+ //
+
+ switch (irpSp->MinorFunction) {
+
+ case IRP_MN_QUERY_DIRECTORY:
+
+ status = NwQueryDirectory( IrpContext, icb );
+ break;
+
+ case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
+
+#if 0
+ if ( !icb->FailedFindNotify ) {
+ icb->FailedFindNotify = TRUE;
+#endif
+ status = STATUS_NOT_SUPPORTED;
+#if 0
+ } else {
+
+ //
+ // HACKHACK
+ // Cover for moronic process that keeps trying to use
+ // find notify even though we don't support it.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+
+ if ( Irp->Cancel ) {
+ status = STATUS_CANCELLED;
+ } else {
+ InsertTailList( &FnList, &IrpContext->NextRequest );
+ IoMarkIrpPending( Irp );
+ IoSetCancelRoutine( Irp, NwCancelFindNotify );
+ status = STATUS_PENDING;
+ }
+
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+ NwReleaseRcb( &NwRcb );
+
+ }
+#endif
+
+ break;
+
+ default:
+
+ //
+ // For all other minor function codes we say they're invalid
+ // and complete the request.
+ //
+
+ DebugTrace(0, Dbg, "Invalid FS Control Minor Function Code %08lx\n", irpSp->MinorFunction);
+
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ } finally {
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ NwReleaseFcb( dcb->NonPagedFcb );
+ DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryDirectory (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This is the work routine for querying a directory.
+
+Arugments:
+
+ IrpContext - Supplies the Irp context information.
+
+ Icb - Pointer the ICB for the request.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation.
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ PUCHAR buffer;
+ CLONG systemBufferLength;
+ UNICODE_STRING searchMask;
+ ULONG fileIndexLow;
+ ULONG fileIndexHigh;
+ FILE_INFORMATION_CLASS fileInformationClass;
+ BOOLEAN restartScan;
+ BOOLEAN returnSingleEntry;
+ BOOLEAN indexSpecified;
+ PVCB vcb;
+
+ BOOLEAN ansiStringAllocated = FALSE;
+ UCHAR SearchAttributes;
+ BOOLEAN searchRetry;
+
+ static WCHAR star[] = L"*";
+
+ BOOLEAN caseInsensitive = TRUE; //*** Make searches case insensitive
+
+ ULONG lastEntry;
+ ULONG nextEntry;
+ ULONG totalBufferLength;
+
+ PFILE_BOTH_DIR_INFORMATION dirInfo;
+ PFILE_NAMES_INFORMATION namesInfo;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+ vcb = Icb->SuperType.Fcb->Vcb;
+
+ DebugTrace(+1, Dbg, "NwQueryDirectory\n", 0 );
+ DebugTrace( 0, Dbg, "Icb = %08lx\n", (ULONG)Icb);
+ DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+ DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
+ DebugTrace( 0, Dbg, "Search Mask = %08lx\n", (ULONG)irpSp->Parameters.QueryDirectory.FileName);
+ DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex);
+ DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass);
+ DebugTrace( 0, Dbg, "RestartScan = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN));
+ DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY));
+ DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED));
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ systemBufferLength = irpSp->Parameters.QueryDirectory.Length;
+
+ if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
+ fileIndexLow = 0;
+ } else {
+ //
+ // Tell the gateway we do support resume from index so long
+ // as the index returned is the same as the last file we
+ // returned. Otherwise the SMB server does a brute force rewind
+ // on each find next.
+ //
+
+ fileIndexLow = irpSp->Parameters.QueryDirectory.FileIndex;
+ }
+ fileIndexHigh = 0;
+
+ fileInformationClass =
+ irpSp->Parameters.QueryDirectory.FileInformationClass;
+
+ restartScan = BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN);
+ indexSpecified = BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
+ returnSingleEntry = BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
+
+ if (irpSp->Parameters.QueryDirectory.FileName != NULL) {
+ searchMask = *(PUNICODE_STRING)irpSp->Parameters.QueryDirectory.FileName;
+ } else {
+ searchMask.Length = 0;
+ searchMask.Buffer = NULL;
+ }
+
+ buffer = Irp->UserBuffer;
+ DebugTrace(0, Dbg, "Users Buffer -> %08lx\n", buffer);
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Check if the ICB already has a query template attached. If it
+ // does not already have one then we either use the string we are
+ // given or we attach our own containing "*"
+ //
+
+ if ( Icb->NwQueryTemplate.Buffer == NULL ) {
+
+ //
+ // This is our first time calling query directory so we need
+ // to either set the query template to the user specified string
+ // or to "*.*".
+ //
+
+ if ( searchMask.Buffer == NULL ) {
+
+ DebugTrace(0, Dbg, "Set template to *", 0);
+
+ searchMask.Length = sizeof( star ) - sizeof(WCHAR);
+ searchMask.Buffer = star;
+
+ }
+
+ DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG)&searchMask);
+
+ //
+ // Map the NT search names to NCP. Note that this must be
+ // done after the Unicode to OEM translation.
+ //
+
+ searchRetry = FALSE;
+
+ do {
+
+ status = NtSearchMaskToNw(
+ &searchMask,
+ &Icb->NwQueryTemplate,
+ Icb,
+ searchRetry );
+
+ if ( !NT_SUCCESS( status ) ) {
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
+ return( status );
+ }
+
+ Icb->UQueryTemplate.Buffer = ALLOCATE_POOL( PagedPool, searchMask.Length );
+ if (Icb->UQueryTemplate.Buffer == NULL ) {
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Icb->UQueryTemplate.MaximumLength = searchMask.Length;
+ RtlCopyUnicodeString( &Icb->UQueryTemplate, &searchMask );
+
+ //
+ // Now send a Search Initialize NCP.
+ //
+ // Do a short search if the server doesn't support long names,
+ // or this is a short-name non-wild card search
+ //
+
+ if ( !Icb->ShortNameSearch ) {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Lbb-DbC",
+ NCP_LFN_SEARCH_INITIATE,
+ vcb->Specific.Disk.LongNameSpace,
+ vcb->Specific.Disk.VolumeNumber,
+ vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbee",
+ &Icb->SearchVolume,
+ &Icb->SearchIndexHigh,
+ &Icb->SearchIndexLow );
+ }
+
+ } else {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbJ",
+ NCP_SEARCH_INITIATE,
+ vcb->Specific.Disk.Handle,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbww-",
+ &Icb->SearchVolume,
+ &Icb->SearchHandle,
+ &Icb->SearchIndexLow );
+ }
+
+ }
+
+ //
+ // If we couldn't find the search path, and we did a long
+ // name search initiate, try again with a short name.
+ //
+
+ if ( status == STATUS_OBJECT_PATH_NOT_FOUND &&
+ !Icb->ShortNameSearch ) {
+
+ searchRetry = TRUE;
+
+ if ( Icb->UQueryTemplate.Buffer != NULL ) {
+ FREE_POOL( Icb->UQueryTemplate.Buffer );
+ }
+
+ RtlFreeOemString ( &Icb->NwQueryTemplate );
+
+ } else {
+ searchRetry = FALSE;
+ }
+
+
+ } while ( searchRetry );
+
+ if ( !NT_SUCCESS( status ) ) {
+ if (status == STATUS_UNSUCCESSFUL) {
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_SUCH_FILE);
+ return( STATUS_NO_SUCH_FILE );
+ }
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
+ return( status );
+ }
+
+ //
+ // Since we are doing a search we will need to send an End Of Job
+ // for this PID.
+ //
+
+ NwSetEndOfJobRequired( Icb->Pid );
+
+ fileIndexLow = Icb->SearchIndexLow;
+ fileIndexHigh = Icb->SearchIndexHigh;
+
+ //
+ // We can't ask for both files and directories, so first ask for
+ // files, then ask for directories.
+ //
+
+ SearchAttributes = NW_ATTRIBUTE_SYSTEM |
+ NW_ATTRIBUTE_HIDDEN |
+ NW_ATTRIBUTE_READ_ONLY;
+
+ //
+ // If there are no wildcards in the search mask, then setup to
+ // not generate the . and .. entries.
+ //
+
+ if ( !FsRtlDoesNameContainWildCards( &Icb->UQueryTemplate ) ) {
+ Icb->DotReturned = TRUE;
+ Icb->DotDotReturned = TRUE;
+ } else {
+ Icb->DotReturned = FALSE;
+ Icb->DotDotReturned = FALSE;
+ }
+
+
+ } else {
+
+ //
+ // Check if we were given an index to start with or if we need to
+ // restart the scan or if we should use the index that was saved in
+ // the ICB.
+ //
+
+ if (restartScan) {
+ fileIndexLow = (ULONG)-1;
+ fileIndexHigh = Icb->SearchIndexHigh;
+
+ //
+ // Send a Search Initialize NCP. The server often times out search
+ // handles and if this one has been sitting at the end of the
+ // directory then its likely we would get no files at all!
+ //
+ // Do a short search if the server doesn't support long names,
+ // or this is a short-name non-wild card search
+ //
+
+ if ( !Icb->ShortNameSearch ) {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Lbb-DbC",
+ NCP_LFN_SEARCH_INITIATE,
+ vcb->Specific.Disk.LongNameSpace,
+ vcb->Specific.Disk.VolumeNumber,
+ vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbee",
+ &Icb->SearchVolume,
+ &Icb->SearchIndexHigh,
+ &Icb->SearchIndexLow );
+ }
+
+ } else {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbJ",
+ NCP_SEARCH_INITIATE,
+ vcb->Specific.Disk.Handle,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbww-",
+ &Icb->SearchVolume,
+ &Icb->SearchHandle,
+ &Icb->SearchIndexLow );
+ }
+
+ }
+
+ Icb->ReturnedSomething = FALSE;
+
+ //
+ // We can't ask for both files and directories, so first ask for
+ // files, then ask for directories.
+ //
+
+ SearchAttributes = NW_ATTRIBUTE_SYSTEM |
+ NW_ATTRIBUTE_HIDDEN |
+ NW_ATTRIBUTE_READ_ONLY;
+ Icb->SearchAttributes = SearchAttributes;
+
+ Icb->DotReturned = FALSE;
+ Icb->DotDotReturned = FALSE;
+
+ } else if ((!indexSpecified) ||
+ ((fileIndexLow == Icb->SearchIndexLow) &&
+ (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart))) {
+ //
+ // Continue from the last filename. If an index is specified then its
+ // only allowed for the gateway (and other system services).
+ //
+
+ fileIndexLow = Icb->SearchIndexLow;
+ fileIndexHigh = Icb->SearchIndexHigh;
+ SearchAttributes = Icb->SearchAttributes;
+
+ if ( SearchAttributes == 0xFF ) {
+
+ //
+ // This is a completed search.
+ //
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_MORE_FILES);
+ return( STATUS_NO_MORE_FILES );
+ }
+
+ } else {
+
+ //
+ // SVR to avoid rescanning from end of dir all
+ // the way through the directory again.
+ //
+
+ if ((Icb->SearchIndexLow == -1) &&
+ (Icb->LastSearchIndexLow == fileIndexLow) &&
+ (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart) &&
+ (Icb->SearchAttributes == 0xFF )) {
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory SVR exit-> %08lx\n", STATUS_NO_MORE_FILES);
+ return( STATUS_NO_MORE_FILES );
+ }
+ // SVR end
+
+ //
+ // Someone's trying to do a resume from key. The netware
+ // server doesn't support this, so neither do we.
+ //
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NOT_IMPLEMENTED);
+ return( STATUS_NOT_IMPLEMENTED );
+ }
+
+ }
+
+ //
+ // Now we are committed to completing the Irp, we do that in
+ // the finally clause of the following try.
+ //
+
+ try {
+
+ ULONG baseLength;
+ ULONG lengthAdded;
+ NW_DIRECTORY_INFO nwDirInfo;
+ ULONG FileNameLength;
+
+ lastEntry = 0;
+ nextEntry = 0;
+
+ switch (fileInformationClass) {
+
+ case FileDirectoryInformation:
+
+ baseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
+ break;
+
+ case FileFullDirectoryInformation:
+
+ baseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] );
+ break;
+
+ case FileNamesInformation:
+
+ baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] );
+ break;
+
+ case FileBothDirectoryInformation:
+
+ baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] );
+ break;
+
+ default:
+
+ try_return( status = STATUS_INVALID_INFO_CLASS );
+ }
+
+ //
+ // It is not ok to attempt a reconnect if this request fails with a
+ // connection error, since our search handle would be invalid.
+ //
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ while ( TRUE ) {
+
+ ULONG bytesToCopy;
+ ULONG bytesRemainingInBuffer;
+
+ DebugTrace(0, Dbg, "Top of Loop\n", 0);
+ DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", fileIndexLow);
+ DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry);
+ DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry);
+
+ nwDirInfo.FileName.Buffer = nwDirInfo.FileNameBuffer;
+ nwDirInfo.FileName.MaximumLength = NW_MAX_FILENAME_SIZE;
+
+ status = GetNextFile(
+ pIrpContext,
+ Icb,
+ &fileIndexLow,
+ &fileIndexHigh,
+ SearchAttributes,
+ &nwDirInfo );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ DebugTrace(0, Dbg, "DirFileName = %wZ\n", &nwDirInfo.FileName);
+ DebugTrace(0, Dbg, "FileIndexLow = %08lx\n", fileIndexLow);
+
+ FileNameLength = nwDirInfo.FileName.Length;
+ bytesRemainingInBuffer = systemBufferLength - nextEntry;
+
+ ASSERT( bytesRemainingInBuffer >= baseLength );
+
+ //
+ // See how much of the name we will be able to copy into
+ // the system buffer. This also dictates our return
+ // value.
+ //
+
+ if ( baseLength + FileNameLength <= bytesRemainingInBuffer ) {
+
+ bytesToCopy = FileNameLength;
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ bytesToCopy = bytesRemainingInBuffer - baseLength;
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // Note how much of buffer we are consuming and zero
+ // the base part of the structure.
+ //
+
+ lengthAdded = baseLength + bytesToCopy;
+ RtlZeroMemory( &buffer[nextEntry], baseLength );
+
+ switch (fileInformationClass) {
+
+ case FileBothDirectoryInformation:
+
+ //
+ // Fill in the short name, if this is a LFN volume.
+ //
+
+ DebugTrace(0, Dbg, "Getting directory both information\n", 0);
+
+#if 0
+ if ( nwDirInfo.DosDirectoryEntry != 0xFFFF &&
+ !IsFatNameValid( &nwDirInfo.FileName ) ) {
+
+ UNICODE_STRING ShortName;
+
+ status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SbDb",
+ NCP_DIR_FUNCTION, NCP_GET_SHORT_NAME,
+ Icb->SearchVolume,
+ nwDirInfo.DosDirectoryEntry,
+ 0 );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
+
+ //
+ // Short name is in form 8.3 plus nul terminator.
+ //
+
+ ShortName.MaximumLength = 13 * sizeof(WCHAR) ;
+ ShortName.Buffer = dirInfo->ShortName;
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N_P",
+ 15,
+ &ShortName );
+
+ if ( NT_SUCCESS( status ) ) {
+ dirInfo->ShortNameLength = (CCHAR)ShortName.Length;
+ }
+ }
+ }
+#endif
+
+ case FileFullDirectoryInformation:
+
+ //
+ // We don't use EaLength, so fill in nothing here.
+ //
+
+ DebugTrace(0, Dbg, "Getting directory full information\n", 0);
+
+ case FileDirectoryInformation:
+
+ DebugTrace(0, Dbg, "Getting directory information\n", 0);
+
+ //
+ // The eof indicates the number of instances and
+ // allocation size is the maximum allowed
+ //
+
+ dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
+
+ dirInfo->FileAttributes = nwDirInfo.Attributes;
+ dirInfo->FileNameLength = bytesToCopy;
+ dirInfo->EndOfFile.LowPart = nwDirInfo.FileSize;
+ dirInfo->EndOfFile.HighPart = 0;
+ dirInfo->AllocationSize = dirInfo->EndOfFile;
+ dirInfo->CreationTime = NwDateTimeToNtTime( nwDirInfo.CreationDate, nwDirInfo.CreationTime );
+ dirInfo->LastAccessTime = NwDateTimeToNtTime( nwDirInfo.LastAccessDate, 0 );
+ dirInfo->LastWriteTime = NwDateTimeToNtTime( nwDirInfo.LastUpdateDate, nwDirInfo.LastUpdateTime );
+ dirInfo->ChangeTime = dirInfo->LastWriteTime;
+ if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
+ dirInfo->FileIndex = 0;
+ } else {
+ dirInfo->FileIndex = fileIndexLow;
+ }
+ break;
+
+ case FileNamesInformation:
+
+ DebugTrace(0, Dbg, "Getting names information\n", 0);
+
+
+ namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry];
+
+ namesInfo->FileNameLength = FileNameLength;
+ if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
+ namesInfo->FileIndex = 0;
+ } else {
+ namesInfo->FileIndex = fileIndexLow;
+ }
+
+ break;
+
+ default:
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+
+ RtlMoveMemory( &buffer[nextEntry + baseLength],
+ nwDirInfo.FileName.Buffer,
+ bytesToCopy );
+
+ dump( Dbg, &buffer[nextEntry], lengthAdded);
+ //
+ // Setup the previous next entry offset.
+ //
+
+ *((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry;
+ totalBufferLength = nextEntry + lengthAdded;
+
+ //
+ // Set ourselves up for the next iteration
+ //
+
+ lastEntry = nextEntry;
+ nextEntry += (ULONG)QuadAlign( lengthAdded );
+
+ //
+ // Check if the last entry didn't completely fit
+ //
+
+ if ( status == STATUS_BUFFER_OVERFLOW ) {
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Check if we are only to return a single entry
+ //
+
+ if (returnSingleEntry) {
+ try_return( status = STATUS_SUCCESS );
+ }
+
+ } else {
+
+ //
+ // The search response contained an error. If we have
+ // not yet enumerated directories, do them now. Otherwise,
+ // we are done searching for files.
+ //
+
+ if ( status == STATUS_UNSUCCESSFUL &&
+ !FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
+
+ SetFlag( SearchAttributes, NW_ATTRIBUTE_DIRECTORY );
+ fileIndexLow = (ULONG)-1;
+ continue;
+
+ } else {
+
+ //
+ // Remember that this is a completed search and
+ // quit the loop.
+ //
+
+ SearchAttributes = 0xFF;
+ break;
+ }
+ }
+
+ //
+ // Here are the rules concerning filling up the buffer:
+ //
+ // 1. The Io system garentees that there will always be
+ // enough room for at least one base record.
+ //
+ // 2. If the full first record (including file name) cannot
+ // fit, as much of the name as possible is copied and
+ // STATUS_BUFFER_OVERFLOW is returned.
+ //
+ // 3. If a subsequent record cannot completely fit into the
+ // buffer, none of it (as in 0 bytes) is copied, and
+ // STATUS_SUCCESS is returned. A subsequent query will
+ // pick up with this record.
+ //
+ // Since we cannot rewind a search, we'll guess that the
+ // next entry is a full length name. If it mightn't fix,
+ // just bail and re the files we've got.
+ //
+
+ bytesRemainingInBuffer = systemBufferLength - nextEntry;
+
+ if ( baseLength + NW_MAX_FILENAME_SIZE > bytesRemainingInBuffer ) {
+
+ DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
+ try_return( status = STATUS_SUCCESS );
+ }
+
+ } // while ( TRUE )
+
+ try_exit: NOTHING;
+ } finally {
+
+ //
+ // At this point we're finished searching for files.
+ // If the NextEntry is zero then we haven't found anything so we
+ // will return no more files or no such file.
+ //
+
+ if ( status == STATUS_NO_MORE_FILES ||
+ status == STATUS_UNSUCCESSFUL ||
+ status == STATUS_SUCCESS ) {
+ if (nextEntry == 0) {
+ if (Icb->ReturnedSomething) {
+ status = STATUS_NO_MORE_FILES;
+ } else {
+ status = STATUS_NO_SUCH_FILE;
+ }
+ } else {
+ Icb->ReturnedSomething = TRUE;
+ status = STATUS_SUCCESS;
+ }
+
+ }
+
+ //
+ // Indicate how much of the system buffer we have used up.
+ //
+
+ Irp->IoStatus.Information = totalBufferLength;
+
+ //
+ // Remember the last file index, so that we can resume this
+ // search.
+ //
+
+
+ // SVR to avoid rescanning from end of dir all
+
+ if (fileIndexLow != -1) {
+ Icb->LastSearchIndexLow = fileIndexLow;
+ }
+ // SVR end
+
+ Icb->SearchIndexLow = fileIndexLow;
+ Icb->SearchIndexHigh = fileIndexHigh;
+
+ Icb->SearchAttributes = SearchAttributes;
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
+ }
+
+ return status;
+}
+
+NTSTATUS
+GetNextFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB Icb,
+ PULONG FileIndexLow,
+ PULONG FileIndexHigh,
+ UCHAR SearchAttributes,
+ PNW_DIRECTORY_INFO DirInfo
+ )
+/*++
+
+Routine Description:
+
+ Get the next file in the directory being searched.
+
+Arguments:
+
+ pIrpContext - Supplies the request being processed.
+
+ Icb - A pointer to the ICB for the directory to query.
+
+ FileIndexLow, FileIndexHigh - On entry, the the index of the
+ previous directory entry. On exit, the index to the directory
+ entry returned.
+
+ SearchAttributes - Search attributes to use.
+
+ DirInfo - Returns information for the directory entry found.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+{
+ NTSTATUS status;
+ PVCB vcb;
+
+ static UNICODE_STRING DotFile = { 2, 2, L"." };
+ static UNICODE_STRING DotDotFile = { 4, 4, L".." };
+
+ PAGED_CODE();
+
+ DirInfo->DosDirectoryEntry = 0xFFFF;
+
+ if ( !Icb->DotReturned ) {
+
+ Icb->DotReturned = TRUE;
+
+ //
+ // Return '.' only if it we are not searching in the root directory
+ // and it matches the search pattern.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
+ FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotFile, TRUE, NULL ) ) {
+
+ RtlCopyUnicodeString( &DirInfo->FileName, &DotFile );
+ DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
+ DirInfo->FileSize = 0;
+ DirInfo->CreationDate = DEFAULT_DATE;
+ DirInfo->LastAccessDate = DEFAULT_DATE;
+ DirInfo->LastUpdateDate = DEFAULT_DATE;
+ DirInfo->LastUpdateTime = DEFAULT_TIME;
+ DirInfo->CreationTime = DEFAULT_TIME;
+
+ return( STATUS_SUCCESS );
+ }
+ }
+
+ if ( !Icb->DotDotReturned ) {
+
+ Icb->DotDotReturned = TRUE;
+
+ //
+ // Return '..' only if it we are not searching in the root directory
+ // and it matches the search pattern.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
+ FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotDotFile, TRUE, NULL ) ) {
+
+ RtlCopyUnicodeString( &DirInfo->FileName, &DotDotFile );
+ DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
+ DirInfo->FileSize = 0;
+ DirInfo->CreationDate = DEFAULT_DATE;
+ DirInfo->LastAccessDate = DEFAULT_DATE;
+ DirInfo->LastUpdateDate = DEFAULT_DATE;
+ DirInfo->LastUpdateTime = DEFAULT_TIME;
+ DirInfo->CreationTime = DEFAULT_TIME;
+
+ return( STATUS_SUCCESS );
+ }
+ }
+
+ vcb = Icb->SuperType.Fcb->Vcb;
+ if ( Icb->ShortNameSearch ) {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Fbwwbp",
+ NCP_SEARCH_CONTINUE,
+ Icb->SearchVolume,
+ Icb->SearchHandle,
+ *(PUSHORT)FileIndexLow,
+ SearchAttributes,
+ Icb->NwQueryTemplate.Buffer
+ );
+
+ if ( !NT_SUCCESS( status )) {
+ return status;
+ }
+
+ *FileIndexLow = 0;
+ *FileIndexHigh = 0;
+
+ if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nw=Rb-ww",
+ FileIndexLow,
+ &DirInfo->FileName, 14,
+ &DirInfo->Attributes,
+ &DirInfo->CreationDate,
+ &DirInfo->CreationTime
+ );
+
+#if 0
+ if ( DirInfo->CreationDate == 0 && DirInfo->CreationTime == 0 ) {
+ DirInfo->CreationDate = DEFAULT_DATE;
+ DirInfo->CreationTime = DEFAULT_TIME;
+ }
+#endif
+
+ DirInfo->FileSize = 0;
+ DirInfo->LastAccessDate = DirInfo->CreationDate;
+ DirInfo->LastUpdateDate = DirInfo->CreationDate;
+ DirInfo->LastUpdateTime = DirInfo->CreationTime;
+
+ } else {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nw=Rb-dwwww",
+ FileIndexLow,
+ &DirInfo->FileName, 14,
+ &DirInfo->Attributes,
+ &DirInfo->FileSize,
+ &DirInfo->CreationDate,
+ &DirInfo->LastAccessDate,
+ &DirInfo->LastUpdateDate,
+ &DirInfo->LastUpdateTime
+ );
+
+ DirInfo->CreationTime = DEFAULT_TIME;
+ }
+
+ } else {
+
+ status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDDp",
+ NCP_LFN_SEARCH_CONTINUE,
+ vcb->Specific.Disk.LongNameSpace,
+ 0, // Data stream
+ SearchAttributes & SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME |
+ LFN_FLAG_INFO_DIR_INFO |
+ LFN_FLAG_INFO_NAME,
+ vcb->Specific.Disk.VolumeNumber,
+ *FileIndexHigh,
+ *FileIndexLow,
+ Icb->NwQueryTemplate.Buffer );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N-ee_e_e_xx_xx_x_e_P",
+ FileIndexHigh,
+ FileIndexLow,
+ 5,
+ &DirInfo->Attributes,
+ 2,
+ &DirInfo->FileSize,
+ 6,
+ &DirInfo->CreationTime,
+ &DirInfo->CreationDate,
+ 4,
+ &DirInfo->LastUpdateTime,
+ &DirInfo->LastUpdateDate,
+ 4,
+ &DirInfo->LastAccessDate,
+ 14,
+ &DirInfo->DosDirectoryEntry,
+ 20,
+ &DirInfo->FileName );
+ }
+
+ if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
+ DirInfo->FileSize = 0;
+ }
+
+ }
+
+ if ( DirInfo->Attributes == 0 ) {
+ DirInfo->Attributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NtSearchMaskToNw(
+ IN PUNICODE_STRING UcSearchMask,
+ IN OUT POEM_STRING OemSearchMask,
+ IN PICB Icb,
+ IN BOOLEAN ShortNameSearch
+ )
+/*++
+
+Routine Description:
+
+ This routine maps a netware path name to the correct netware format.
+
+Arguments:
+
+ UcSearchMask - The search mask in NT format.
+
+ OemSearchMask - The search mask in Netware format.
+
+ Icb - The ICB of the directory in which we are searching.
+
+ ShortNameSearch - If TRUE, always do a short name search.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ USHORT i;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // Use a short name search if the volume does not support long names.
+ // or this is a short name ICB, and we are doing a short name, non
+ // wild-card search.
+ //
+
+ if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ||
+
+ ShortNameSearch ||
+
+ ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
+ !FsRtlDoesNameContainWildCards( UcSearchMask ) &&
+ IsFatNameValid( UcSearchMask ) ) ) {
+
+ Icb->ShortNameSearch = TRUE;
+
+ //
+ // Allocate space for and initialize the query templates.
+ //
+
+ status = RtlUpcaseUnicodeStringToOemString(
+ OemSearchMask,
+ UcSearchMask,
+ TRUE );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ //
+ // Special case. Map '*.*' to '*'.
+ //
+
+ if ( OemSearchMask->Length == 3 &&
+ RtlCompareMemory( OemSearchMask->Buffer, "*.*", 3 ) == 3 ) {
+
+ OemSearchMask->Length = 1;
+ OemSearchMask->Buffer[1] = '\0';
+
+ } else {
+
+
+ for ( i = 0; i < OemSearchMask->Length ; i++ ) {
+
+ if( FsRtlIsLeadDbcsCharacter( OemSearchMask->Buffer[i] ) ) {
+
+ i++; // Skip to the trailing byte.
+
+ if (( Japan ) &&
+ ((UCHAR)(OemSearchMask->Buffer[i]) == 0x5C )) {
+
+ //
+ // The trailbyte is 0x5C, replace it with 0x13
+ //
+
+
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x13 );
+ continue;
+
+ }
+
+ } else {
+
+ // Single byte character that may need modification.
+
+ switch ( (UCHAR)(OemSearchMask->Buffer[i]) ) {
+
+ case ANSI_DOS_STAR:
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '*' );
+ break;
+
+ case ANSI_DOS_QM:
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '?' );
+ break;
+
+ case ANSI_DOS_DOT:
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '.' );
+ break;
+
+ //
+ // Netware Japanese version The following character is
+ // replaced with another one if the string is for File
+ // Name only when sendding from Client to Server.
+ //
+ // SO U+0xFF7F SJIS+0xBF -> 0x10
+ // SMALL_YO U+0xFF6E SJIS+0xAE -> 0x11
+ // SMALL_E U+0xFF64 SJIS+0xAA -> 0x12
+ //
+ // The reason is unknown, Should ask Novell Japan.
+ //
+ // See Also exchange.c
+
+ case 0xBF: // ANSI_DOS_KATAKANA_SO:
+ if (Japan) {
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
+ }
+ break;
+
+ case 0xAE: // ANSI_DOS_KATAKANA_SMALL_YO:
+ if (Japan) {
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
+ }
+ break;
+
+ case 0xAA: // ANSI_DOS_KATAKANA_SMALL_E:
+ if (Japan) {
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
+ }
+ break;
+
+ }
+ }
+ }
+ }
+
+ } else {
+
+ USHORT size;
+ PCHAR buffer;
+ UNICODE_STRING src;
+ OEM_STRING dest;
+
+ Icb->ShortNameSearch = FALSE;
+
+ //
+ // Allocate space for and initialize the query templates.
+ //
+
+#ifndef QFE_BUILD
+ buffer = ExAllocatePoolWithTag( PagedPool,
+ UcSearchMask->Length,
+ 'scwn' );
+#else
+ buffer = ExAllocatePool( PagedPool,
+ UcSearchMask->Length );
+#endif
+ if ( buffer == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ OemSearchMask->Buffer = buffer;
+
+ //
+ // Special case. Map '????????.???' to '*'.
+ //
+
+ if ( UcSearchMask->Length == 24 &&
+ RtlCompareMemory( UcSearchMask->Buffer, L">>>>>>>>\">>>", 24 ) == 24 ) {
+
+ OemSearchMask->Length = 3;
+ OemSearchMask->Buffer[0] = (UCHAR)0xFF;
+ OemSearchMask->Buffer[1] = '*';
+ OemSearchMask->Buffer[2] = '\0';
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Now convert the string, character by character
+ //
+
+ src.Buffer = UcSearchMask->Buffer;
+ src.Length = 2;
+ dest.Buffer = buffer;
+ dest.MaximumLength = UcSearchMask->Length;
+
+ size = UcSearchMask->Length / 2;
+
+ for ( i = 0; i < size ; i++ ) {
+ switch ( *src.Buffer ) {
+
+ case L'*':
+ case L'?':
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)*src.Buffer++;
+ break;
+
+ case L'.':
+ *dest.Buffer++ = (UCHAR)*src.Buffer++;
+ break;
+
+ case DOS_DOT:
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)( 0x80 | '.' );
+ *src.Buffer++;
+ break;
+
+ case DOS_STAR:
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)( 0x80 | '*' );
+ *src.Buffer++;
+ break;
+
+ case DOS_QM:
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)( 0x80 | '?' );
+ *src.Buffer++;
+ break;
+
+ default:
+ RtlUnicodeStringToCountedOemString( &dest, &src, FALSE );
+ dest.Buffer++;
+ src.Buffer++;
+ }
+ }
+
+ *dest.Buffer = '\0';
+ OemSearchMask->Length = (USHORT)( dest.Buffer - buffer );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#if 0
+VOID
+NwCancelFindNotify (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the cancel function for an find notify IRP.
+
+Arguments:
+
+ DeviceObject - ignored
+
+ Irp - Supplies the Irp being cancelled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY listEntry;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // We now need to void the cancel routine and release the io cancel
+ // spin-lock.
+ //
+
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) {
+
+ PIRP_CONTEXT IrpContext;
+
+ IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ if ( IrpContext->pOriginalIrp == Irp ) {
+ RemoveEntryList( &IrpContext->NextRequest );
+ NwCompleteRequest( IrpContext, STATUS_CANCELLED );
+ break;
+ }
+ }
+
+ NwReleaseRcb( &NwRcb );
+}
+#endif
+
+
diff --git a/private/nw/rdr/encrypt.c b/private/nw/rdr/encrypt.c
new file mode 100644
index 000000000..ee7758621
--- /dev/null
+++ b/private/nw/rdr/encrypt.c
@@ -0,0 +1,322 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ encrypt.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to mangle an objectid, challenge key and
+ password such that a NetWare server will accept the
+ password as valid.
+
+ This program uses information published in Byte Magazine.
+
+Author:
+
+ Colin Watson [ColinW] 15-Mar-1993
+
+Revision History:
+
+--*/
+
+#include <procs.h>
+
+
+UCHAR Table[] = {
+ 0x78, 0x08, 0x64, 0xe4, 0x5c, 0x17, 0xbf, 0xa8,
+ 0xf8, 0xcc, 0x94, 0x1e, 0x46, 0x24, 0x0a, 0xb9,
+ 0x2f, 0xb1, 0xd2, 0x19, 0x5e, 0x70, 0x02, 0x66,
+ 0x07, 0x38, 0x29, 0x3f, 0x7f, 0xcf, 0x64, 0xa0,
+ 0x23, 0xab, 0xd8, 0x3a, 0x17, 0xcf, 0x18, 0x9d,
+ 0x91, 0x94, 0xe4, 0xc5, 0x5c, 0x8b, 0x23, 0x9e,
+ 0x77, 0x69, 0xef, 0xc8, 0xd1, 0xa6, 0xed, 0x07,
+ 0x7a, 0x01, 0xf5, 0x4b, 0x7b, 0xec, 0x95, 0xd1,
+ 0xbd, 0x13, 0x5d, 0xe6, 0x30, 0xbb, 0xf3, 0x64,
+ 0x9d, 0xa3, 0x14, 0x94, 0x83, 0xbe, 0x50, 0x52,
+ 0xcb, 0xd5, 0xd5, 0xd2, 0xd9, 0xac, 0xa0, 0xb3,
+ 0x53, 0x69, 0x51, 0xee, 0x0e, 0x82, 0xd2, 0x20,
+ 0x4f, 0x85, 0x96, 0x86, 0xba, 0xbf, 0x07, 0x28,
+ 0xc7, 0x3a, 0x14, 0x25, 0xf7, 0xac, 0xe5, 0x93,
+ 0xe7, 0x12, 0xe1, 0xf4, 0xa6, 0xc6, 0xf4, 0x30,
+ 0xc0, 0x36, 0xf8, 0x7b, 0x2d, 0xc6, 0xaa, 0x8d } ;
+
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, RespondToChallenge )
+#pragma alloc_text( PAGE, Shuffle )
+#pragma alloc_text( PAGE, Scramble )
+#endif
+
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+ UCHAR achBuf[32];
+
+ PAGED_CODE();
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ Shuffle( &pChallenge[0], achBuf, 16, &achK[0] );
+ Shuffle( &pChallenge[4], achBuf, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ PAGED_CODE();
+
+ //
+ // Truncate all trailing zeros from the password.
+ //
+
+ while (iPasswordLen > 0 && szUpperPassword[iPasswordLen-1] == 0 ) {
+ iPasswordLen--;
+ }
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ unsigned int offset = achTemp[iOutputIndex << 1],
+ shift = (offset & 0x1) ? 0 : 4 ;
+
+ achOutputBuffer[iOutputIndex] =
+ (Table[offset >> 1] >> shift) & 0xF ;
+
+ offset = achTemp[(iOutputIndex << 1)+1],
+ shift = (offset & 0x1) ? 4 : 0 ;
+
+ achOutputBuffer[iOutputIndex] |=
+ (Table[offset >> 1] << shift) & 0xF0;
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ PAGED_CODE();
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
diff --git a/private/nw/rdr/errorlog.c b/private/nw/rdr/errorlog.c
new file mode 100644
index 000000000..599d25521
--- /dev/null
+++ b/private/nw/rdr/errorlog.c
@@ -0,0 +1,201 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ errorlog.c
+
+Abstract:
+
+ This module implements the error logging in the netware redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 11-Feb-92
+
+Revision History:
+
+--*/
+
+#include <procs.h>
+#include <align.h>
+
+#include <stdarg.h>
+
+ULONG
+SequenceNumber = 0;
+
+#ifdef ALLOC_PRAGMA
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, Error )
+#endif
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+ VOID
+_cdecl
+Error(
+ IN ULONG UniqueErrorCode,
+ IN NTSTATUS NtStatusCode,
+ IN PVOID ExtraInformationBuffer,
+ IN USHORT ExtraInformationLength,
+ IN USHORT NumberOfInsertionStrings,
+ ...
+ )
+
+#define LAST_NAMED_ARGUMENT NumberOfInsertionStrings
+
+/*++
+
+Routine Description:
+
+ This function allocates an I/O error log record, fills it in and writes it
+ to the I/O error log.
+
+Arguments:
+
+ UniqueErrorCode - The event code
+
+ NtStatusCode - The NT status of the failure
+
+ ExtraInformationBuffer - Raw data for the event
+
+ ExtraInformationLength - The length of the raw data
+
+ NumberOfInsertionString - The number of insertion strings that follow
+
+ InsertionString - 0 or more insertion strings.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ PIO_ERROR_LOG_PACKET ErrorLogEntry;
+ int TotalErrorLogEntryLength;
+ ULONG SizeOfStringData = 0;
+ va_list ParmPtr; // Pointer to stack parms.
+
+ if (NumberOfInsertionStrings != 0) {
+ USHORT i;
+
+ va_start(ParmPtr, LAST_NAMED_ARGUMENT);
+
+ for (i = 0; i < NumberOfInsertionStrings; i += 1) {
+ PWSTR String = va_arg(ParmPtr, PWSTR);
+ SizeOfStringData += (wcslen(String) + 1) * sizeof(WCHAR);
+ }
+ }
+
+ //
+ // Ideally we want the packet to hold the servername and ExtraInformation.
+ // Usually the ExtraInformation gets truncated.
+ //
+
+ TotalErrorLogEntryLength =
+ min( ExtraInformationLength + sizeof(IO_ERROR_LOG_MESSAGE) + 1 + SizeOfStringData,
+ ERROR_LOG_MAXIMUM_SIZE );
+
+ ErrorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
+ FileSystemDeviceObject,
+ (UCHAR)TotalErrorLogEntryLength
+ );
+
+ if (ErrorLogEntry != NULL) {
+ PCHAR DumpData;
+ ULONG RemainingSpace = TotalErrorLogEntryLength - sizeof( IO_ERROR_LOG_MESSAGE );
+ USHORT i;
+ ULONG SizeOfRawData;
+
+ if (RemainingSpace > SizeOfStringData) {
+ SizeOfRawData = RemainingSpace - SizeOfStringData;
+ } else {
+ SizeOfStringData = RemainingSpace;
+
+ SizeOfRawData = 0;
+ }
+
+ //
+ // Fill in the error log entry
+ //
+
+ ErrorLogEntry->ErrorCode = UniqueErrorCode;
+ ErrorLogEntry->MajorFunctionCode = 0;
+ ErrorLogEntry->RetryCount = 0;
+ ErrorLogEntry->UniqueErrorValue = 0;
+ ErrorLogEntry->FinalStatus = NtStatusCode;
+ ErrorLogEntry->IoControlCode = 0;
+ ErrorLogEntry->DeviceOffset.LowPart = 0;
+ ErrorLogEntry->DeviceOffset.HighPart = 0;
+ ErrorLogEntry->SequenceNumber = (ULONG)SequenceNumber ++;
+ ErrorLogEntry->StringOffset =
+ (USHORT)ROUND_UP_COUNT(
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + SizeOfRawData,
+ ALIGN_WORD);
+
+ DumpData = (PCHAR)ErrorLogEntry->DumpData;
+
+ //
+ // Append the extra information. This information is typically
+ // an SMB header.
+ //
+
+ if (( ARGUMENT_PRESENT( ExtraInformationBuffer )) &&
+ ( SizeOfRawData != 0 )) {
+ ULONG Length;
+
+ Length = min(ExtraInformationLength, (USHORT)SizeOfRawData);
+ RtlCopyMemory(
+ DumpData,
+ ExtraInformationBuffer,
+ Length);
+ ErrorLogEntry->DumpDataSize = (USHORT)Length;
+ } else {
+ ErrorLogEntry->DumpDataSize = 0;
+ }
+
+ ErrorLogEntry->NumberOfStrings = 0;
+
+ if (NumberOfInsertionStrings != 0) {
+ PWSTR StringOffset = (PWSTR)((PCHAR)ErrorLogEntry + ErrorLogEntry->StringOffset);
+ PWSTR InsertionString;
+
+ //
+ // Set up ParmPtr to point to first of the caller's parameters.
+ //
+
+ va_start(ParmPtr, LAST_NAMED_ARGUMENT);
+
+ for (i = 0 ; i < NumberOfInsertionStrings ; i+= 1) {
+ InsertionString = va_arg(ParmPtr, PWSTR);
+
+ if (((wcslen(InsertionString) + 1) * sizeof(WCHAR)) <= SizeOfStringData ) {
+
+ wcscpy(StringOffset, InsertionString);
+
+ StringOffset += wcslen(InsertionString) + 1;
+
+ SizeOfStringData -= (wcslen(InsertionString) + 1) * sizeof(WCHAR);
+
+ ErrorLogEntry->NumberOfStrings += 1;
+
+ }
+
+ }
+
+ }
+
+ IoWriteErrorLogEntry(ErrorLogEntry);
+ }
+
+}
+
+
diff --git a/private/nw/rdr/except.c b/private/nw/rdr/except.c
new file mode 100644
index 000000000..0c82cc863
--- /dev/null
+++ b/private/nw/rdr/except.c
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Except.c
+
+Abstract:
+
+ This module implements the exception handling for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
+
+#if 0 // Not pageable
+NwExceptionFilter
+NwProcessException
+#endif
+
+LONG
+NwExceptionFilter (
+ IN PIRP Irp,
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to decide if we should or should not handle
+ an exception status that is being raised. It inserts the status
+ into the IrpContext and either indicates that we should handle
+ the exception or bug check the system.
+
+Arguments:
+
+ ExceptionCode - Supplies the exception code to being checked.
+
+Return Value:
+
+ ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
+
+--*/
+
+{
+ NTSTATUS ExceptionCode;
+#ifdef NWDBG
+ PVOID ExceptionAddress;
+ ExceptionAddress = ExceptionPointer->ExceptionRecord->ExceptionAddress;
+#endif
+
+ ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
+ DebugTrace(0, DEBUG_TRACE_UNWIND, "NwExceptionFilter %X\n", ExceptionCode);
+#ifdef NWDBG
+ DebugTrace(0, DEBUG_TRACE_UNWIND, " %X\n", ExceptionAddress);
+#endif
+
+ //
+ // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code
+ // from the exception record.
+ //
+
+ if (ExceptionCode == STATUS_IN_PAGE_ERROR) {
+ if (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) {
+ ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
+ }
+ }
+
+ if (FsRtlIsNtstatusExpected( ExceptionCode )) {
+
+ DebugTrace(0, DEBUG_TRACE_UNWIND, "Exception expected\n", 0);
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ } else {
+
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+}
+
+NTSTATUS
+NwProcessException (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS ExceptionCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine process an exception. It either completes the request
+ with the saved exception status or it sends it off to IoRaiseHardError()
+
+Arguments:
+
+ IrpContext - Supplies the Irp being processed
+
+ ExceptionCode - Supplies the normalized exception status being handled
+
+Return Value:
+
+ NTSTATUS - Returns the results of either posting the Irp or the
+ saved completion status.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+
+ DebugTrace(0, Dbg, "NwProcessException\n", 0);
+
+ Irp = IrpContext->pOriginalIrp;
+ Irp->IoStatus.Status = ExceptionCode;
+
+ //
+ // If the error is a hard error, or verify required, then we will complete
+ // it if this is a recursive Irp, or with a top-level Irp, either send
+ // it to the Fsp for verification, or send it to IoRaiseHardError, who
+ // will deal with it.
+ //
+
+ if (ExceptionCode == STATUS_CANT_WAIT) {
+
+ Status = NwPostToFsp( IrpContext, TRUE );
+
+ } else {
+
+ //
+ // We got an error, so zero out the information field before
+ // completing the request if this was an input operation.
+ // Otherwise IopCompleteRequest will try to copy to the user's buffer.
+ //
+
+ if ( FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) {
+
+ Irp->IoStatus.Information = 0;
+ }
+
+ Status = ExceptionCode;
+
+ }
+
+ return Status;
+}
+
diff --git a/private/nw/rdr/exchange.c b/private/nw/rdr/exchange.c
new file mode 100644
index 000000000..92535c02c
--- /dev/null
+++ b/private/nw/rdr/exchange.c
@@ -0,0 +1,4887 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ exchange.c
+
+Abstract:
+
+ This module implements the File Create routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Hans Hurvig [hanshu] Aug-1992 Created
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+#include "tdikrnl.h"
+#include <STDARG.H>
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+//
+// Exchange.c Global constants
+//
+
+// broadcast to socket 0x0452
+
+TA_IPX_ADDRESS SapBroadcastAddress =
+ {
+ 1,
+ sizeof(TA_IPX_ADDRESS), TDI_ADDRESS_TYPE_IPX,
+ 0, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, SAP_SOCKET
+ };
+
+UCHAR SapPacketType = PACKET_TYPE_SAP;
+UCHAR NcpPacketType = PACKET_TYPE_NCP;
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+#ifdef NWDBG
+ULONG DropCount = 0;
+int AlwaysAllocateIrp = 1;
+#endif
+
+NTSTATUS
+CompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+FspGetMessage(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+CompletionWatchDogSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ );
+
+NTSTATUS
+FormatRequest(
+ PIRP_CONTEXT pIrpC,
+ PEX pEx,
+ char* f,
+ va_list a // format specific parameters
+ );
+
+VOID
+ScheduleReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+ReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+CopyIndicatedData(
+ PIRP_CONTEXT pIrpContext,
+ PCHAR RspData,
+ ULONG BytesIndicated,
+ PULONG BytesTaken,
+ ULONG ReceiveDatagramFlags
+ );
+
+NTSTATUS
+AllocateReceiveIrp(
+ PIRP_CONTEXT pIrpContext,
+ PVOID ReceiveData,
+ ULONG BytesAvailable,
+ PULONG BytesAccepted,
+ PNW_TDI_STRUCT pTdiStruct
+ );
+
+NTSTATUS
+ReceiveIrpCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+NTSTATUS
+FspProcessServerDown(
+ PIRP_CONTEXT IrpContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NextSocket )
+#pragma alloc_text( PAGE, ExchangeWithWait )
+#pragma alloc_text( PAGE, NewRouteRetry )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, FspGetMessage )
+#pragma alloc_text( PAGE1, Exchange )
+#pragma alloc_text( PAGE1, BuildRequestPacket )
+#pragma alloc_text( PAGE1, ParseResponse )
+#pragma alloc_text( PAGE1, ParseNcpResponse )
+#pragma alloc_text( PAGE1, FormatRequest )
+#pragma alloc_text( PAGE1, PrepareAndSendPacket )
+#pragma alloc_text( PAGE1, PreparePacket )
+#pragma alloc_text( PAGE1, SendPacket )
+#pragma alloc_text( PAGE1, AppendToScbQueue )
+#pragma alloc_text( PAGE1, KickQueue )
+#pragma alloc_text( PAGE1, SendNow )
+#pragma alloc_text( PAGE1, SetEvent )
+#pragma alloc_text( PAGE1, CompletionSend )
+#pragma alloc_text( PAGE1, CopyIndicatedData )
+#pragma alloc_text( PAGE1, AllocateReceiveIrp )
+#pragma alloc_text( PAGE1, ReceiveIrpCompletion )
+#pragma alloc_text( PAGE1, VerifyResponse )
+#pragma alloc_text( PAGE1, ScheduleReconnectRetry )
+#pragma alloc_text( PAGE1, ReconnectRetry )
+#pragma alloc_text( PAGE1, NewRouteBurstRetry )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+ServerDatagramHandler
+WatchDogDatagramHandler
+SendDatagramHandler
+CompletionWatchDogSend
+MdlLength
+FreeReceiveIrp
+FspProcessServerDown
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+NTSTATUS
+_cdecl
+Exchange(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine is a wrapper for _Exchange. See the comment
+ in _Exchange for routine and argument description.
+
+--*/
+
+{
+ va_list Arguments;
+ NTSTATUS Status;
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // We won't be completing this IRP now, so mark it pending.
+ //
+
+ IoMarkIrpPending( pIrpContext->pOriginalIrp );
+
+ //
+ // Start the packet on it's way to the wire.
+ //
+
+ Status = PrepareAndSendPacket( pIrpContext );
+
+ return( Status );
+}
+
+NTSTATUS
+_cdecl
+BuildRequestPacket(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine is a wrapper for FormatRequest. See the comment
+ in FormatRequest for routine and argument description.
+
+--*/
+
+{
+ va_list Arguments;
+ NTSTATUS Status;
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PIRP_CONTEXT IrpContext,
+ PUCHAR Response,
+ ULONG ResponseLength,
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ pIrpC - Supplies the irp context for the exchange request. This may
+ be NULL for generic packet types.
+
+ f... - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'B' Burst primary response ( byte * )
+ 'N' NCP response ( void )
+ 'S' Burst secondary response ( byte * )
+ 'G' Generic packet ( )
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'x' ordered word ( word* )
+ 'd' hi-lo dword ( dword* )
+ 'e' ordered dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'p' pstring to Unicode ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+ Added 3/29/95 by CoryWest:
+
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ 'S' unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 'T' terminal unicode string copy as NDS_STRING (UNICODE_STRING *)
+
+ 't' terminal unicode string with the nds null copied
+ as NDS_STRING (UNICODE_STRING *) (for GetUseName)
+
+ Not in use:
+
+ 's' cstring copy as NDS_STRING (char* / char *, word)
+ 'V' sized NDS value ( byte **, dword *)
+ 'l' what's this?
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+
+ PEPresponse *pResponseParameters;
+ PCHAR FormatByte;
+ va_list Arguments;
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS NcpStatus;
+ ULONG Length;
+
+ va_start( Arguments, FormatString );
+
+ //
+ // Make sure that we have an IrpContext unless we are doing
+ // a scan of a generic packet.
+ //
+
+#ifdef NWDBG
+ if ( *FormatString != 'G' ) {
+ ASSERT( IrpContext != NULL );
+ }
+#endif
+
+ switch ( *FormatString ) {
+
+ //
+ // NCP response.
+ //
+
+ case 'N':
+
+ Length = 8; // The data begins 8 bytes into the packet
+
+ pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1);
+
+ //
+ // If there's a message pending for us on the server and we have
+ // popups disabled, we won't pick it up, but we should continue
+ // processing NCPs correctly!
+ //
+
+ if ( ( pResponseParameters->status == 0 ) ||
+ ( pResponseParameters->status == 0x40 ) ) {
+ Status = NwErrorToNtStatus( pResponseParameters->error );
+ } else {
+ Status = NwConnectionStatusToNtStatus( pResponseParameters->status );
+ if ( Status == STATUS_REMOTE_DISCONNECT ) {
+ Stats.ServerDisconnects++;
+ IrpContext->pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+ }
+
+ break;
+
+ //
+ // Burst response, first packet
+ //
+
+ case 'B': // BUGBUG Not needed, cleanup write.c first.
+ {
+ PNCP_BURST_HEADER BurstResponse = (PNCP_BURST_HEADER)Response;
+
+ byte* b = va_arg ( Arguments, byte* );
+ ULONG Result;
+ ULONG Offset = BurstResponse->BurstOffset;
+ *b = BurstResponse->Flags;
+
+ Length = 28; // The data begins 28 bytes into the packet
+
+ if ( Offset == 0 ) {
+
+ //
+ // This is the first packet in the burst response. Look
+ // at the result code.
+ //
+ // Note that the result DWORD is in lo-hi order.
+ //
+
+ Result = *(ULONG UNALIGNED *)(Response + 36);
+
+ switch ( Result ) {
+
+ case 0:
+ case 3: // No data
+ break;
+
+ case 1:
+ Status = STATUS_DISK_FULL;
+ break;
+
+ case 2: // I/O error
+ Status = STATUS_UNEXPECTED_IO_ERROR;
+ break;
+
+ default:
+ Status = NwErrorToNtStatus( (UCHAR)Result );
+ break;
+
+ }
+ }
+
+ break;
+ }
+
+#if 0
+ //
+ // Burst response, secondary packet
+ //
+
+ case 'S':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ *b = Response[2];
+
+ Length = 28; // The data begins 28 bytes into the packet
+ break;
+ }
+#endif
+
+ case 'G':
+ Length = 0; // The data begins at the start of the packet
+ break;
+
+ default:
+ ASSERT( FALSE );
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ //
+ // If this packet contains an error, simply return the error.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ NcpStatus = Status;
+
+ FormatByte = FormatString + 1;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ word l = va_arg ( Arguments, word );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ *b = Response[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'x':
+ {
+ word* w = va_arg ( Arguments, word* );
+ *w = *(word UNALIGNED *)&Response[Length];
+ Length += 2;
+ break;
+ }
+
+ case 'd':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ b[3] = Response[Length++];
+ b[2] = Response[Length++];
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'e':
+ {
+ dword UNALIGNED * d = va_arg ( Arguments, dword* );
+ *d = *(dword UNALIGNED *)&Response[Length];
+ Length += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ word l = strlen( &Response[Length] );
+ memcpy ( c, &Response[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ byte l = Response[Length++];
+ memcpy ( c, &Response[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Response[Length++];
+ OemString.Buffer = &Response[Length];
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemString.Length != 0 ) {
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+
+ if (!NT_SUCCESS( Status )) {
+ pUString->Length = 0;
+ NcpStatus = Status;
+ }
+
+ } else {
+ pUString->Length = 0;
+ }
+
+
+ break;
+ }
+
+ case 'r':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ word l = va_arg ( Arguments, word );
+ TdiCopyLookaheadData( b, &Response[Length], l, 0);
+ Length += l;
+ break;
+ }
+
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Response[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemString.Length != 0) {
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+
+ if (!NT_SUCCESS( Status )) {
+
+ ASSERT( Status == STATUS_BUFFER_OVERFLOW );
+ pUString->Length = 0;
+ NcpStatus = Status;
+ }
+
+ } else {
+ pUString->Length = 0;
+ }
+
+ Length += len;
+ break;
+ }
+
+ case 'W':
+ {
+
+ WORD *w = va_arg ( Arguments, WORD* );
+ *w = (* (WORD *)&Response[Length]);
+ Length += 2;
+ break;
+
+ }
+
+ case 'D':
+ {
+
+ DWORD *d = va_arg ( Arguments, DWORD* );
+ *d = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ break;
+
+ }
+
+ case 'S':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+
+ //
+ // Don't count the null terminator that is part of
+ // Novell's counted unicode string.
+ //
+
+ pU->Length = strl - sizeof( WCHAR );
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 's':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 'T':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+ strl -= sizeof( WCHAR ); // Don't count the NULL from NDS.
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+ }
+
+ }
+
+ break;
+
+ }
+
+ case 't':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ /*
+ case 's':
+ {
+
+ char *c = va_arg( Arguments, char * );
+ WORD l = va_arg( Arguments, WORD );
+ ULONG len = (* (DWORD *)&Response[Length]);
+ Length += 4;
+
+ // BUGBUG: How to fix this?
+ // l = WideCharToMultiByte(CP_ACP,0,(WCHAR *)&Response[Length],Length/2,c,l,0,0);
+ // if (!l) {
+ // #ifdef NWDBG
+ // DbgPrint( "ParseResponse case s couldnt translate from WCHAR.\n" );
+ // #endif
+ // goto ErrorExit;
+ // }
+
+ len = ROUNDUP4(len);
+ Length += len;
+ break;
+
+ }
+ case 'V':
+ {
+
+ BYTE **b = va_arg( Arguments, BYTE **);
+ DWORD *pLen = va_arg ( Arguments, DWORD *);
+ DWORD len = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ if (b) {
+ *b = (BYTE *)&Response[Length];
+ }
+ if (pLen) {
+ *pLen = len;
+ }
+ Length += ROUNDUP4(len);
+ break;
+
+ }
+
+ case 'l':
+ {
+
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ BYTE* w = va_arg ( Arguments, BYTE* );
+ WORD i;
+
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+
+ for ( i = 0; i < ((WORD) *b); i++, w += sizeof(WORD) )
+ {
+ w[1] = Response[Length++];
+ w[0] = Response[Length++];
+ }
+
+ break;
+ }
+ */
+
+#ifdef NWDBG
+ default:
+ DbgPrintf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ DbgBreakPoint();
+#endif
+ }
+
+ if ( Length > ResponseLength ) {
+#ifdef NWDBG
+ DbgPrintf ( "*****exchange: not enough response data, %d\n", Length );
+
+ if ( IrpContext ) {
+
+ Error( EVENT_NWRDR_INVALID_REPLY,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ IrpContext->pNpScb->ServerName.Buffer );
+
+ }
+#endif
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+ }
+
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+
+ return( NcpStatus );
+}
+
+NTSTATUS
+ParseNcpResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_RESPONSE Response
+ )
+{
+ NTSTATUS Status;
+
+ if ( Response->Status == 0 ) {
+ Status = NwErrorToNtStatus( Response->Error );
+ } else {
+ Status = NwConnectionStatusToNtStatus( Response->Status );
+ if ( Status == STATUS_REMOTE_DISCONNECT ) {
+ Stats.ServerDisconnects++;
+ IrpContext->pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+FormatRequest(
+ PIRP_CONTEXT pIrpC,
+ PEX pEx,
+ char* f,
+ va_list a // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by f and the additional parameters. When a
+ valid response has been received call pEx with the resonse.
+
+ An exchange is a generic way of assembling a request packet of a
+ given type, containing a set of fields, sending the packet, receiving
+ a response packet, and disassembling the fields of the response packet.
+
+ The packet type and each field is specified by individual
+ characters in a format string.
+
+ The exchange procedure takes such a format string plus additional
+ parameters as necessary for each character in the string as specified
+ below.
+
+Arguments: '']
+
+ pIrpC - supplies the irp context for the exchange request.
+
+ pEx - supplies the routine to process the data.
+
+ f... - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'B' NCP burst ( dword, dword, byte )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'N' NCP subfunction w/o size ( byte, byte )
+ 'D' NCP disconnect ( void )
+ 'E' Echo data ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ 'W' lo-hi word ( word / word* )
+ 'D' lo-hi dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'J' variant of U ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'v' cstring ( UNICODE_STRING* )
+ 'r' raw bytes ( byte*, word )
+ 'w' fixed length unicode ( UNICODE_STRING*, word )
+ 'C' Component format name, with count ( UNICODE_STRING * )
+ 'N' Component format name, no count ( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_SUCCESS.
+
+--*/
+{
+ NTSTATUS status;
+ char* z;
+ word data_size;
+ PNONPAGED_SCB pNpScb = pIrpC->pNpScb;
+ dword dwData;
+
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT );
+ ASSERT( pIrpC->pNpScb != NULL );
+
+ status= STATUS_LINK_FAILED;
+
+ pIrpC->pEx = pEx; // Routine to process reply
+ pIrpC->Destination = pNpScb->RemoteAddress;
+ ClearFlag( pIrpC->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+
+ switch ( *f ) {
+
+ case 'A':
+ // Send to local network (0), a broadcast (-1), socket 0x452
+ pIrpC->Destination = SapBroadcastAddress;
+ pIrpC->PacketType = SAP_BROADCAST;
+
+ data_size = 0;
+ pNpScb->RetryCount = 3;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+ SetFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+ break;
+
+ case 'E':
+ pIrpC->Destination = pNpScb->EchoAddress;
+ pIrpC->PacketType = NCP_ECHO;
+
+ //
+ // For echo packets use a short timeout and a small retry count.
+ // Set the retry send bit, so that SendNow doesn't reset the
+ // RetryCount to a bigger number.
+ //
+
+ pNpScb->RetryCount = 0;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 7;
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+ SetFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+ SetFlag( pIrpC->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ data_size = 0;
+ break;
+
+ case 'C':
+ pIrpC->PacketType = NCP_CONNECT;
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_CONNECT;
+ pIrpC->req[2] = 0x00;
+ pIrpC->req[3] = 0xFF;
+ pIrpC->req[4] = 0x00;
+ pIrpC->req[5] = 0xFF;
+ data_size = 6;
+
+ pNpScb->MaxTimeOut = 16 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = 4 * pNpScb->TickCount + 10;
+ pNpScb->SequenceNo = 0;
+ break;
+
+ case 'F':
+ pIrpC->PacketType = NCP_FUNCTION;
+ goto FallThrough;
+
+ case 'S':
+ case 'N':
+ pIrpC->PacketType = NCP_SUBFUNCTION;
+ goto FallThrough;
+
+ case 'L':
+ pIrpC->PacketType = NCP_SUBFUNCTION;
+ goto FallThrough;
+
+ case 'D':
+ pIrpC->PacketType = NCP_DISCONNECT;
+ FallThrough:
+ if ( *f == 'D' ) {
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_DISCONNECT;
+ } else {
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_REQUEST;
+ }
+
+ pNpScb->RetryCount = DefaultRetryCount ;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+
+ //
+ // Mark this packet as SequenceNumberRequired. We need to guarantee
+ // the packets are sent in sequence number order, so we will
+ // fill in the sequence number when we are ready to send the
+ // packet.
+ //
+
+ SetFlag( pIrpC->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ pIrpC->req[3] = pNpScb->ConnectionNo;
+ pIrpC->req[5] = pNpScb->ConnectionNoHigh;
+
+ if ( pIrpC->Icb != NULL && pIrpC->Icb->Pid != INVALID_PID ) {
+ pIrpC->req[4] = (UCHAR)pIrpC->Icb->Pid;
+ } else {
+ pIrpC->req[4] = 0xFF;
+ }
+
+ data_size = 6;
+
+ if ( *f == 'L' ) {
+ pIrpC->req[data_size++] = NCP_LFN_FUNCTION;
+ }
+
+ if ( *f != 'D' ) {
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ if ( *f == 'S' ) {
+ data_size += 2;
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ if ( *f == 'N' ) {
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ break;
+
+ case 'B':
+ pIrpC->PacketType = NCP_BURST;
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_BURST;
+
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+
+ if ( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) {
+ pNpScb->RetryCount = 20;
+ }
+
+ pIrpC->req[3] = 0x2; // Stream Type = Big Send Burst
+
+ *(PULONG)&pIrpC->req[4] = pNpScb->SourceConnectionId;
+ *(PULONG)&pIrpC->req[8] = pNpScb->DestinationConnectionId;
+
+
+ LongByteSwap( (*(PULONG)&pIrpC->req[16]) , pNpScb->CurrentBurstDelay ); // Send delay time
+ dwData = va_arg( a, dword ); // Size of data
+ LongByteSwap( pIrpC->req[24], dwData );
+ dwData = va_arg( a, dword ); // Offset of data
+ LongByteSwap( pIrpC->req[28], dwData );
+ pIrpC->req[2] = va_arg( a, byte ); // Burst flags
+
+ data_size = 34;
+
+ break;
+
+ default:
+ DbgPrintf ( "*****exchange: invalid packet type, %x\n", *f );
+ DbgBreakPoint();
+ va_end( a );
+ return status;
+ }
+
+ z = f;
+ while ( *++z && *z != 'f' )
+ {
+ switch ( *z )
+ {
+ case '=':
+ pIrpC->req[data_size++] = 0;
+ case '-':
+ pIrpC->req[data_size++] = 0;
+ break;
+
+ case '_':
+ {
+ word l = va_arg ( a, word );
+ ASSERT( data_size + l <= MAX_SEND_DATA );
+
+ while ( l-- )
+ pIrpC->req[data_size++] = 0;
+ break;
+ }
+
+ case 's':
+ {
+ word l = va_arg ( a, word );
+ ASSERT ( data_size + l <= MAX_SEND_DATA );
+ data_size += l;
+ break;
+ }
+
+ case 'i':
+ pIrpC->req[4] = va_arg ( a, byte );
+ break;
+
+ case 'b':
+ pIrpC->req[data_size++] = va_arg ( a, byte );
+ break;
+
+ case 'w':
+ {
+ word w = va_arg ( a, word );
+ pIrpC->req[data_size++] = (byte) (w >> 8);
+ pIrpC->req[data_size++] = (byte) (w >> 0);
+ break;
+ }
+
+
+ case 'd':
+ {
+ dword d = va_arg ( a, dword );
+ pIrpC->req[data_size++] = (byte) (d >> 24);
+ pIrpC->req[data_size++] = (byte) (d >> 16);
+ pIrpC->req[data_size++] = (byte) (d >> 8);
+ pIrpC->req[data_size++] = (byte) (d >> 0);
+ break;
+ }
+
+ case 'W':
+ {
+ word w = va_arg ( a, word );
+ *(word UNALIGNED *)&pIrpC->req[data_size] = w;
+ data_size += 2;
+ break;
+ }
+
+
+ case 'D':
+ {
+ dword d = va_arg ( a, dword );
+ *(dword UNALIGNED *)&pIrpC->req[data_size] = d;
+ data_size += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+ word l = strlen( c );
+ ASSERT (data_size + l <= MAX_SEND_DATA );
+
+ RtlCopyMemory( &pIrpC->req[data_size], c, l+1 );
+ data_size += l + 1;
+ break;
+ }
+
+ case 'v':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT (( data_size + Length <= MAX_SEND_DATA) && ( (Length & 0xffffff00) == 0) );
+
+ OemString.Buffer = &pIrpC->req[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)Length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+ byte l = strlen( c );
+
+ if ((data_size+l>MAX_SEND_DATA) ||
+ ( (l & 0xffffff00) != 0) ) {
+
+ ASSERT("***exchange: Packet too long!2!\n" && FALSE );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ pIrpC->req[data_size++] = l;
+ RtlCopyMemory( &pIrpC->req[data_size], c, l );
+ data_size += l;
+ break;
+ }
+
+ case 'J':
+ case 'U':
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ PUCHAR pOemString;
+ ULONG Length;
+ ULONG i;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if (( data_size + Length > MAX_SEND_DATA ) ||
+ ( (Length & 0xffffff00) != 0) ) {
+ ASSERT("***exchange:Packet too long or name >255 chars!4!\n" && FALSE);
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ pIrpC->req[data_size++] = (UCHAR)Length;
+ OemString.Buffer = &pIrpC->req[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ if ( *z == 'u' ) {
+ status = RtlUnicodeStringToCountedOemString(
+ &OemString,
+ pUString,
+ FALSE );
+ } else {
+ status = RtlUpcaseUnicodeStringToCountedOemString(
+ &OemString,
+ pUString,
+ FALSE );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ data_size += (USHORT)Length;
+
+ if (( Japan ) &&
+ ( *z == 'J' )) {
+
+ //
+ // Netware Japanese version The following single byte character is replaced with another one
+ // if the string is for File Name only when sending from Client to Server.
+ //
+ // U+0xFF7F SJIS+0xBF -> 0x10
+ // U+0xFF6E SJIS+0xAE -> 0x11
+ // U+0xFF64 SJIS+0xAA -> 0x12
+ //
+
+ for ( i = 0 , pOemString = OemString.Buffer ; i < Length ; i++ , pOemString++ ) {
+
+ if( FsRtlIsLeadDbcsCharacter( *pOemString ) ) {
+
+ // Skip the trailing byte
+
+ i++; pOemString++;
+
+ if (*pOemString == 0x5C ) {
+
+ //
+ // The trailbyte is 0x5C, replace it with 0x13
+ //
+
+ *pOemString = 0x13;
+
+ }
+
+ } else {
+
+ // Single byte character that may need modification.
+
+
+ if ( *pOemString == 0xBF ) {
+
+ *pOemString = 0x10;
+
+ } else if ( *pOemString == 0xAA ) {
+
+ *pOemString = 0x12;
+
+ } else if ( *pOemString == 0xAE ) {
+
+ *pOemString = 0x11;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ case 'r':
+ {
+ byte* b = va_arg ( a, byte* );
+ word l = va_arg ( a, word );
+ if (data_size+l>MAX_SEND_DATA) {
+ ASSERT("***exchange: Packet too long!6!\n"&& FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+ RtlCopyMemory( &pIrpC->req[data_size], b, l );
+ data_size += l;
+ break;
+ }
+
+ case 'x':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ ULONG RequiredLength = va_arg( a, word );
+ ULONG Length;
+ OEM_STRING OemString;
+
+ //
+ // Convert this string to an OEM string.
+ //
+
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, TRUE );
+ ASSERT( NT_SUCCESS( status ));
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if ( data_size + RequiredLength > MAX_SEND_DATA ) {
+ ASSERT("***exchange: Packet too long!4!\n" && FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Copy the oem string to the buffer, padded with 0's if
+ // necessary.
+ //
+
+ Length = MIN( OemString.Length, RequiredLength );
+ RtlMoveMemory( &pIrpC->req[data_size], OemString.Buffer, Length );
+
+ if ( RequiredLength > Length ) {
+ RtlFillMemory(
+ &pIrpC->req[data_size+Length],
+ RequiredLength - Length,
+ 0 );
+ }
+
+ RtlFreeAnsiString(&OemString);
+
+ data_size += (USHORT)RequiredLength;
+ break;
+ }
+
+ case 'C':
+ case 'N':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ PWCH thisChar, lastChar, firstChar;
+ PCHAR componentCountPtr, pchar;
+ CHAR componentCount;
+ UNICODE_STRING UnicodeString;
+ int i;
+
+ //
+ // Copy the oem string to the buffer, in component format.
+ //
+
+ thisChar = pUString->Buffer;
+ lastChar = &pUString->Buffer[ pUString->Length / sizeof(WCHAR) ];
+
+ //
+ // Skip leading path separators
+ //
+
+ while ( *thisChar == OBJ_NAME_PATH_SEPARATOR &&
+ thisChar < lastChar ) {
+ thisChar++;
+ }
+
+ componentCount = 0;
+ if ( *z == 'C' ) {
+ componentCountPtr = &pIrpC->req[data_size++];
+ }
+
+
+ while ( thisChar < lastChar ) {
+
+ if ( data_size >= MAX_SEND_DATA - 1 ) {
+ ASSERT( ("***exchange: Packet too long or name > 255 chars!5!\n" && FALSE) );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ firstChar = thisChar;
+
+ while ( thisChar < lastChar &&
+ *thisChar != OBJ_NAME_PATH_SEPARATOR ) {
+
+ thisChar++;
+
+ }
+
+ ++componentCount;
+
+ UnicodeString.Buffer = firstChar;
+ UnicodeString.Length = ( thisChar - firstChar ) * sizeof(WCHAR);
+
+ OemString.Buffer = &pIrpC->req[data_size + 1];
+ OemString.MaximumLength = MAX_SEND_DATA - data_size - 1;
+
+ status = RtlUnicodeStringToCountedOemString( &OemString, &UnicodeString, FALSE );
+
+ pIrpC->req[data_size] = (UCHAR)OemString.Length;
+ data_size += OemString.Length + 1;
+
+ if ( !NT_SUCCESS( status ) || data_size > MAX_SEND_DATA ) {
+ ASSERT("***exchange: Packet too long or name > 255 chars!5!\n" && FALSE );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ //
+ // Search the result OEM string for the character 0xFF.
+ // If it's there, fail this request. The server doesn't
+ // deal with 0xFF very well.
+ //
+
+ for ( pchar = OemString.Buffer, i = 0;
+ i < OemString.Length;
+ pchar++, i++ ) {
+
+ //
+ // We need to check for dbcs, because 0xff is a
+ // legal trail byte for EUDC characters.
+ //
+ if ( FsRtlIsLeadDbcsCharacter( (UCHAR)*pchar ) ) {
+
+ //
+ // Skip dbcs character.
+ //
+
+ pchar++; i++;
+ continue;
+ }
+
+ if (( (UCHAR)*pchar == LFN_META_CHARACTER ) ||
+ !FsRtlIsAnsiCharacterLegalHpfs(*pchar, FALSE) ) {
+
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ }
+
+ thisChar++; // Skip the path separator
+
+ }
+
+ if ( *z == 'C' ) {
+ *componentCountPtr = componentCount;
+ }
+
+ break;
+ }
+
+ default:
+#ifdef NWDBG
+ DbgPrintf ( "*****exchange: invalid request field, %x\n", *z );
+ DbgBreakPoint();
+#endif
+ ;
+ }
+
+ if ( data_size > MAX_SEND_DATA )
+ {
+ DbgPrintf( "*****exchange: CORRUPT, too much request data\n" );
+ DbgBreakPoint();
+ va_end( a );
+ return STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ pIrpC->TxMdl->ByteCount = data_size;
+
+ if ( *z == 'f' )
+ {
+ PMDL mdl;
+
+ //
+ // Fragment of data following Ipx header. Next parameter is
+ // the address of the mdl describing the fragment.
+ //
+ ++z;
+ mdl = (PMDL) va_arg ( a, byte* );
+ pIrpC->TxMdl->Next = mdl;
+
+ data_size += (USHORT)MdlLength( mdl );
+ }
+
+ if ( *f == 'S' ) {
+
+ pIrpC->req[7] = (data_size-9) >> 8;
+ pIrpC->req[8] = (data_size-9);
+
+ } else if ( *f == 'B' ) {
+
+ //
+ // For burst packets set the number of bytes in this packet to
+ // a real number for burst requests, and to 0 for a missing packet
+ // request.
+ //
+
+ if ( *(PUSHORT)&pIrpC->req[34] == 0 ) {
+ USHORT RealDataSize = data_size - 36;
+ ShortByteSwap( pIrpC->req[32], RealDataSize );
+ } else {
+ *(PUSHORT)&pIrpC->req[32] = 0;
+ }
+ }
+
+ va_end( a );
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+PrepareAndSendPacket(
+ PIRP_CONTEXT pIrpContext
+ )
+{
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ return SendPacket( pIrpContext, pIrpContext->pNpScb );
+}
+
+VOID
+PreparePacket(
+ PIRP_CONTEXT pIrpContext,
+ PIRP pIrp,
+ PMDL pMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine builds the IRP for sending a packet.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+ Irp - The IRP to be used to submit the request to the transport.
+
+ Mdl - A pointer to the MDL for the data to send.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIO_COMPLETION_ROUTINE CompletionRoutine;
+ PNW_TDI_STRUCT pTdiStruct;
+
+ DebugTrace(0, Dbg, "PreparePacket...\n", 0);
+
+ pIrpContext->ConnectionInformation.UserDataLength = 0;
+ pIrpContext->ConnectionInformation.OptionsLength = sizeof( UCHAR );
+ pIrpContext->ConnectionInformation.Options =
+ (pIrpContext->PacketType == SAP_BROADCAST) ?
+ &SapPacketType : &NcpPacketType;
+ pIrpContext->ConnectionInformation.RemoteAddressLength = sizeof(TA_IPX_ADDRESS);
+ pIrpContext->ConnectionInformation.RemoteAddress = &pIrpContext->Destination;
+
+#if NWDBG
+ dump( Dbg,
+ &pIrpContext->Destination.Address[0].Address[0],
+ sizeof(TDI_ADDRESS_IPX));
+ dumpMdl( Dbg, pMdl);
+#endif
+
+ //
+ // Set the socket to use for this send. If unspecified in the
+ // IRP context, use the default (server) socket.
+ //
+
+ pTdiStruct = pIrpContext->pTdiStruct == NULL ?
+ &pIrpContext->pNpScb->Server : pIrpContext->pTdiStruct;
+
+ CompletionRoutine = pIrpContext->CompletionSendRoutine == NULL ?
+ CompletionSend : pIrpContext->CompletionSendRoutine;
+
+ TdiBuildSendDatagram(
+ pIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionRoutine,
+ pIrpContext,
+ pMdl,
+ MdlLength( pMdl ),
+ &pIrpContext->ConnectionInformation );
+
+ //
+ // Set the run routine to send now, only if this is the main IRP
+ // for this irp context.
+ //
+
+ if ( pIrp == pIrpContext->pOriginalIrp ) {
+ pIrpContext->RunRoutine = SendNow;
+ }
+
+ return;
+}
+
+
+NTSTATUS
+SendPacket(
+ PIRP_CONTEXT pIrpC,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue a packet created by exchange and try to send it to the server.
+
+Arguments:
+
+ pIrpC - supplies the irp context for the request creating the socket.
+
+ pNpScb - supplies the server to receive the request.
+
+Return Value:
+
+ STATUS_PENDING
+
+--*/
+{
+ if ( AppendToScbQueue( pIrpC, pNpScb ) ) {
+ KickQueue( pNpScb );
+ }
+
+ return STATUS_PENDING;
+}
+
+
+BOOLEAN
+AppendToScbQueue(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue an IRP context to the SCB, if it is not already there.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context to queue.
+
+ NpScb - Supplies the server to receive the request.
+
+Return Value:
+
+ TRUE - The IRP Context is at the front of the queue.
+ FALSE - The IRP Context is not at the front of the queue.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+#ifdef MSWDBG
+ KIRQL OldIrql;
+#endif
+ DebugTrace(0, Dbg, "AppendToScbQueue... %08lx\n", NpScb);
+ DebugTrace(0, Dbg, "IrpContext = %08lx\n", IrpContext );
+
+ //
+ // Look at the IRP Context flags. If the IRP is already on the
+ // queue, then it must be at the front and ready for processing.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) {
+ ASSERT( NpScb->Requests.Flink == &IrpContext->NextRequest );
+ return( TRUE );
+ }
+
+#ifdef MSWDBG
+ NpScb->RequestQueued = TRUE;
+#endif
+
+#if 0 // Resource layout changed on Daytona. Disable for now.
+
+ //
+ // Make sure that this thread isn't holding the RCB while waiting for
+ // the SCB queue.
+ //
+
+ ASSERT ( NwRcb.Resource.InitialOwnerThreads[0] != (ULONG)PsGetCurrentThread() );
+#endif
+
+ //
+ // The IRP Context was not at the front. Queue it, then look to
+ // see if it was appended to an empty queue.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+#ifdef MSWDBG
+ ExAcquireSpinLock( &NpScb->NpScbSpinLock, &OldIrql );
+ if ( IsListEmpty( &NpScb->Requests ) ) {
+ ListEntry = NULL;
+ } else {
+ ListEntry = NpScb->Requests.Flink;
+ }
+
+ InsertTailList( &NpScb->Requests, &IrpContext->NextRequest );
+ IrpContext->SequenceNumber = NpScb->SequenceNumber++;
+ ExReleaseSpinLock( &NpScb->NpScbSpinLock, OldIrql );
+
+#else
+ ListEntry = ExInterlockedInsertTailList(
+ &NpScb->Requests,
+ &IrpContext->NextRequest,
+ &NpScb->NpScbSpinLock );
+#endif
+
+ if ( ListEntry == NULL ) {
+ ASSERT( NpScb->Requests.Flink == &IrpContext->NextRequest );
+ DebugTrace(-1, Dbg, "AppendToScbQueue -> TRUE\n", 0);
+ return( TRUE );
+ } else {
+ DebugTrace(-1, Dbg, "AppendToScbQueue -> FALSE\n", 0);
+ return( FALSE );
+ }
+
+}
+
+
+VOID
+KickQueue(
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue a packet created by exchange and try to send it to the server.
+
+ Note: NpScbSpinLock must be held before calling this routine.
+
+Arguments:
+
+ pNpScb - supplies the server queue to kick into life.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ PIRP_CONTEXT pIrpC;
+ PRUN_ROUTINE RunRoutine;
+ KIRQL OldIrql;
+
+
+ DebugTrace( +1, Dbg, "KickQueue...%08lx\n", pNpScb);
+
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+ if ( IsListEmpty( &pNpScb->Requests )) {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace( -1, Dbg, " Empty Queue\n", 0);
+ return;
+ }
+
+ pIrpC = CONTAINING_RECORD(pNpScb->Requests.Flink, IRP_CONTEXT, NextRequest);
+
+ ASSERT( pIrpC->pNpScb->Requests.Flink == &pIrpC->NextRequest );
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT);
+
+ RunRoutine = pIrpC->RunRoutine;
+
+ // Only call the routine to tell it it is at the front once
+
+ pIrpC->RunRoutine = NULL;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+
+ //
+ // If the redir is shutting down do not process this request
+ // unless we must.
+ //
+
+ if ( NwRcb.State != RCB_STATE_RUNNING &&
+ !FlagOn( pIrpC->Flags, IRP_FLAG_SEND_ALWAYS ) ) {
+
+ //
+ // Note that it's safe to call the pEx routine without the
+ // spin lock held since this IrpContext just made it to the
+ // front of the queue, and so can't have i/o in progress.
+ //
+
+ if ( pIrpC->pEx != NULL) {
+ pIrpC->pEx( pIrpC, 0, NULL );
+ DebugTrace( -1, Dbg, "KickQueue\n", 0);
+ return;
+ }
+ }
+
+ if ( RunRoutine != NULL ) {
+
+ ASSERT( pNpScb->Receiving == FALSE );
+
+ RunRoutine( pIrpC );
+
+ }
+
+ DebugTrace( -1, Dbg, "KickQueue\n", 0);
+ return;
+}
+
+VOID
+SendNow(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine submits a TDI send request to the tranport layer.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+
+ pNpScb = IrpContext->pNpScb;
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+ pNpScb->RetryCount = DefaultRetryCount;
+ }
+
+ //
+ // Ensure that this IRP Context is really at the front of the queue.
+ //
+
+ ASSERT( pNpScb->Requests.Flink == &IrpContext->NextRequest );
+ IrpContext->RunRoutine = NULL;
+
+ //
+ // Make sure that this is a correctly formatted send request.
+ //
+
+ IrpSp = IoGetNextIrpStackLocation( IrpContext->pOriginalIrp );
+ ASSERT( IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL );
+ ASSERT( IrpSp->MinorFunction == TDI_SEND_DATAGRAM );
+
+ //
+ // This IRP context has a packet ready to send. Send it now.
+ //
+
+ pNpScb->Sending = TRUE;
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE ) ) {
+ pNpScb->OkToReceive = TRUE;
+ }
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ //
+ // If this packet requires a sequence number, set it now.
+ // The sequence number is updated when we receive a response.
+ //
+ // We do not need to synchronize access to SequenceNo since
+ // this is the only active packet for this SCB.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED ) ) {
+ ClearFlag( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ IrpContext->req[2] = pNpScb->SequenceNo;
+ }
+
+ //
+ // If this packet is a burst packet, fill in the burst sequence number
+ // now, and burst request number.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_BURST_PACKET ) ) {
+
+ LongByteSwap( IrpContext->req[12], pNpScb->BurstSequenceNo );
+ pNpScb->BurstSequenceNo++;
+
+ ShortByteSwap( IrpContext->req[20], pNpScb->BurstRequestNo );
+ ShortByteSwap( IrpContext->req[22], pNpScb->BurstRequestNo );
+
+ }
+
+ DebugTrace( +0, Dbg, "Irp %X\n", IrpContext->pOriginalIrp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", IrpContext);
+ DebugTrace( +0, Dbg, "Mdl %X\n", IrpContext->TxMdl);
+
+#if NWDBG
+ dumpMdl( Dbg, IrpContext->TxMdl);
+#endif
+
+ {
+ ULONG len = 0;
+ PMDL Next = IrpContext->TxMdl;
+
+ do {
+ len += MmGetMdlByteCount(Next);
+ } while (Next = Next->Next);
+
+ Stats.BytesTransmitted.QuadPart += len;
+ }
+
+ Status = IoCallDriver(pNpScb->Server.pDeviceObject, IrpContext->pOriginalIrp);
+ DebugTrace( -1, Dbg, "Transport returned: %08lx\n", Status );
+
+ Stats.NcpsTransmitted.QuadPart++;
+
+ return;
+
+}
+
+
+VOID
+SetEvent(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine set the IrpContext Event to the signalled state.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Ensure that this IRP Context is really at the front of the queue.
+ //
+
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ //
+ // This IRP context has a thread waiting to get to the front of
+ // the queue. Set the event to indicate that it can continue.
+ //
+
+#ifdef MSWDBG
+ ASSERT( IrpContext->Event.Header.SignalState == 0 );
+ IrpContext->DebugValue = 0x105;
+#endif
+
+ DebugTrace( +0, Dbg, "Setting event for IrpContext %X\n", IrpContext );
+ NwSetIrpContextEvent( IrpContext );
+}
+
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the byteswapped OldValue++ wrapping from 7fff.
+
+Arguments:
+
+ OldValue - supplies the existing socket number in the range
+ 0x4000 to 0x7fff.
+
+Return Value:
+
+ USHORT OldValue++
+
+--*/
+
+{
+ USHORT TempValue = OldValue + 0x0100;
+
+ if ( TempValue < 0x100 ) {
+ if ( TempValue == 0x007f ) {
+ // Wrap back to 0x4000 from 0xff7f
+ return 0x0040;
+ } else {
+ // Go from something like 0xff40 to 0x0041
+ return TempValue + 1;
+ }
+ }
+ return TempValue;
+}
+
+
+ULONG
+MdlLength (
+ register IN PMDL Mdl
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the number of bytes in an MDL.
+
+Arguments:
+
+ IN PMDL Mdl - Supplies the MDL to determine the length on.
+
+Return Value:
+
+ ULONG - Number of bytes in the MDL
+
+--*/
+
+{
+ register ULONG Size = 0;
+ while (Mdl!=NULL) {
+ Size += MmGetMdlByteCount(Mdl);
+ Mdl = Mdl->Next;
+ }
+ return Size;
+}
+
+
+NTSTATUS
+CompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine does not complete the Irp. It is used to signal to a
+ synchronous part of the driver that it can proceed.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ PIRP_CONTEXT pIrpC = (PIRP_CONTEXT) Context;
+ KIRQL OldIrql;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "CompletionSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", pIrpC);
+ DebugTrace( +0, Dbg, "Status %X\n", Irp->IoStatus.Status);
+
+ pNpScb = pIrpC->pNpScb;
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+
+ ASSERT( pNpScb->Sending == TRUE );
+ pNpScb->Sending = FALSE;
+
+ //
+ // If we got a receive indication while waiting for send
+ // completion and the data is all valid, call the receive handler routine now.
+ //
+
+ if ( pNpScb->Received ) {
+
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+
+ pIrpC->pEx(
+ pIrpC,
+ pIrpC->ResponseLength,
+ pIrpC->rsp );
+
+ } else if (( Irp->IoStatus.Status == STATUS_DEVICE_DOES_NOT_EXIST ) ||
+ ( Irp->IoStatus.Status == STATUS_BAD_NETWORK_PATH ) ||
+ ( Irp->IoStatus.Status == STATUS_INVALID_BUFFER_SIZE ) ||
+ ( Irp->IoStatus.Status == STATUS_NETWORK_UNREACHABLE )) {
+
+ //
+ // The send failed.
+ //
+
+ //
+ // FIXFIX I would prefer to use !NT_SUCCESS(Irp->IoStatus.Status) to
+ // get into this code but would need more time to check its ok.
+ //
+
+ //
+ // If this SCB is still flagged okay to receive (how could it not?)
+ // simply call the callback routine to indicate failure.
+ //
+ // If the SendCompletion hasn't happened, set up so that send
+ // completion will call the callback routine.
+ //
+
+ if ( pNpScb->OkToReceive ) {
+
+ pNpScb->OkToReceive = FALSE;
+ ClearFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace(+0, Dbg, "Send failed\n", 0 );
+
+ pIrpC->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpC->pEx( pIrpC, 0, NULL );
+
+ } else {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ } else {
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ DebugTrace( -1, Dbg, "CompletionSend STATUS_MORE_PROCESSING_REQUIRED\n", 0);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+#if NWDBG
+BOOLEAN UseIrpReceive = FALSE;
+#endif
+
+
+NTSTATUS
+ServerDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ NTSTATUS Status = STATUS_DATA_NOT_ACCEPTED;
+ UCHAR PacketType;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+ PIRP_CONTEXT pIrpC;
+ PNW_TDI_STRUCT pTdiStruct;
+ BOOLEAN AcceptPacket = TRUE;
+ PNCP_BURST_READ_RESPONSE pBurstRsp;
+ NTSTATUS BurstStatus;
+
+ *IoRequestPacket = NULL;
+#if DBG
+ pTdiStruct = NULL;
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+
+ DebugTrace(+0, 0, "nwrdr: Invalid Server Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+#if NWDBG
+
+ // Debug only trick to test IRP receive.
+
+ if ( UseIrpReceive ) {
+ BytesIndicated = 0;
+ }
+#endif
+
+ DebugTrace(+1, Dbg, "ServerDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "Server %x\n", pNpScb);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if ( OptionsLength == 1 ) {
+ PacketType = *(PCHAR)Options;
+ DebugTrace(+0, Dbg, "PacketType %x\n", PacketType);
+ } else {
+ DebugTrace(+0, Dbg, "OptionsLength %x\n", OptionsLength);
+#if NWDBG
+ dump( Dbg, Options, OptionsLength );
+#endif
+ }
+
+ KeAcquireSpinLockAtDpcLevel(&pNpScb->NpScbSpinLock );
+
+ if ( !pNpScb->OkToReceive ) {
+
+ //
+ // This SCB is not expecting to receive any data.
+ // Discard this packet.
+ //
+
+#ifdef NWDBG
+ DropCount++;
+#endif
+ DebugTrace(+0, Dbg, "OkToReceive == FALSE - discard packet\n", 0);
+ AcceptPacket = FALSE;
+ goto process_packet;
+ }
+
+ pIrpC = CONTAINING_RECORD(pNpScb->Requests.Flink, IRP_CONTEXT, NextRequest);
+
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT);
+
+ //
+ // Verify that this packet came from where we expect it to come from,
+ // and that is has a minimum size.
+ //
+
+ if ( ( pIrpC->PacketType != SAP_BROADCAST &&
+ RtlCompareMemory(
+ &pIrpC->Destination,
+ SourceAddress,
+ SourceAddressLength ) != (ULONG)SourceAddressLength ) ||
+ BytesIndicated < 8 ) {
+
+ AcceptPacket = FALSE;
+#ifdef NWDBG
+ DbgPrintf ( "***exchange: stray response tossed\n", 0 );
+#endif
+ goto process_packet;
+ }
+
+ switch ( pIrpC->PacketType ) {
+
+ case SAP_BROADCAST:
+
+ //
+ // We are expected a SAP Broadcast frame. Ensure that this
+ // is a correctly formatted SAP.
+ //
+
+ if ( pIrpC->req[0] != RspData[0] ||
+ pIrpC->req[2] != RspData[2] ||
+ pIrpC->req[3] != RspData[3] ||
+ SourceAddressLength != sizeof(TA_IPX_ADDRESS) ) {
+
+ DbgPrintf ( "***exchange: bad SAP packet\n" );
+ AcceptPacket = FALSE;
+ }
+
+ pTdiStruct = &pNpScb->Server;
+ break;
+
+ case NCP_BURST:
+
+ if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_BURST ) {
+
+ if ( BytesIndicated < 36 ) {
+
+ AcceptPacket = FALSE;
+
+ } else if ( ( RspData[2] & BURST_FLAG_SYSTEM_PACKET ) &&
+ RspData[34] == 0 &&
+ RspData[35] == 0 ) {
+
+ //
+ // We have burst mode busy reponse.
+ //
+
+ DebugTrace(+0, Dbg, "Burst mode busy\n", 0 );
+ NwProcessPositiveAck( pNpScb );
+
+ AcceptPacket = FALSE;
+
+ } else {
+
+ USHORT Brn;
+
+ //
+ // Check the burst sequence number.
+ //
+
+ ShortByteSwap( Brn, RspData[20] );
+
+ if ( pNpScb->BurstRequestNo == Brn ) {
+ pTdiStruct = &pNpScb->Burst;
+ AcceptPacket = TRUE;
+ } else {
+ AcceptPacket = FALSE;
+ }
+ }
+ } else {
+ AcceptPacket = FALSE;
+ }
+
+ break;
+
+ case NCP_ECHO:
+
+ pTdiStruct = &pNpScb->Echo;
+ AcceptPacket = TRUE;
+ break;
+
+ default:
+
+ pTdiStruct = &pNpScb->Server;
+
+ //
+ // This is the handling for all packets types other than
+ // SAP Broadcasts.
+ //
+
+ ASSERT( (pIrpC->PacketType == NCP_CONNECT) ||
+ (pIrpC->PacketType == NCP_FUNCTION) ||
+ (pIrpC->PacketType == NCP_SUBFUNCTION) ||
+ (pIrpC->PacketType == NCP_DISCONNECT));
+
+ if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_ACKNOWLEDGE ) {
+
+ AcceptPacket = FALSE;
+
+ if ( RspData[2] == pIrpC->req[2] &&
+ RspData[3] == pIrpC->req[3] ) {
+
+ //
+ // We have received an ACK frame.
+ //
+
+ DebugTrace(+0, Dbg, "Received positive acknowledge\n", 0 );
+ NwProcessPositiveAck( pNpScb );
+
+ }
+
+ break;
+
+ } else if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_BURST ) {
+
+ //
+ // This is a stray burst response, ignore it.
+ //
+
+ AcceptPacket = FALSE;
+ break;
+
+ } else if ( *(USHORT UNALIGNED *)&RspData[0] != PEP_COMMAND_RESPONSE ) {
+
+ //
+ // We have received an invalid frame.
+ //
+
+ DbgPrintf ( "***exchange: invalid Response\n" );
+ AcceptPacket = FALSE;
+ break;
+
+ } else if ( pIrpC->PacketType == NCP_CONNECT ) {
+
+ pNpScb->SequenceNo = RspData[2];
+ pNpScb->ConnectionNo = RspData[3];
+ pNpScb->ConnectionNoHigh = RspData[5];
+
+ // We should now continue to process the Connect
+ break;
+ }
+
+ //
+ // Make sure this the response we expect.
+ //
+
+ if ( !VerifyResponse( pIrpC, RspData ) ) {
+
+ //
+ // This is a stray or corrupt response. Ignore it.
+ //
+
+ AcceptPacket = FALSE;
+ break;
+
+ } else {
+
+ //
+ // We have received a valid, in sequence response.
+ // Bump the current sequence number.
+ //
+
+ ++pNpScb->SequenceNo;
+
+ }
+
+ if ( pIrpC->PacketType == NCP_FUNCTION ||
+ pIrpC->PacketType == NCP_SUBFUNCTION ) {
+
+ if ( ( RspData[7] &
+ ( NCP_STATUS_BAD_CONNECTION |
+ NCP_STATUS_NO_CONNECTIONS ) ) != 0 ) {
+ //
+ // We've lost our connection to the server.
+ // Try to reconnect if it is allowed for this request.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ if ( BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RECONNECTABLE ) ) {
+ ClearFlag( pIrpC->Flags, IRP_FLAG_RECONNECTABLE );
+ AcceptPacket = FALSE;
+ if (!pNpScb->Sending) {
+ ScheduleReconnectRetry( pIrpC );
+ pNpScb->OkToReceive = FALSE;
+ } else {
+ //
+ // If we are sending, it is not OK schedule the
+ // retry now, because if we do and the send
+ // completion hasnt been run we could end up
+ // with 2 guys thinking they are at the front
+ // of the queue. We let the send complete and
+ // wait for that to fail instead. We will
+ // eventually reconnect.
+ //
+ }
+ }
+
+ break;
+
+ } else if ( ( RspData[7] & NCP_STATUS_SHUTDOWN ) != 0 ) {
+
+ //
+ // This server's going down. We need to process this
+ // message in the FSP. Copy the indicated data and
+ // process in the FSP.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ AcceptPacket = FALSE;
+ pNpScb->OkToReceive = FALSE;
+ pNpScb->Receiving = TRUE;
+
+ CopyIndicatedData(
+ pIrpC,
+ RspData,
+ BytesIndicated,
+ BytesTaken,
+ ReceiveDatagramFlags );
+
+ pIrpC->PostProcessRoutine = FspProcessServerDown;
+ Status = NwPostToFsp( pIrpC, FALSE );
+
+ break;
+ }
+
+ } else if ( pIrpC->PacketType == NCP_DISCONNECT ) {
+
+ //
+ // We have received a disconnect frame.
+ //
+
+ break;
+ }
+
+ }
+
+process_packet:
+ if ( AcceptPacket ) {
+
+ ASSERT ( !IsListEmpty( &pNpScb->Requests ));
+ ASSERT( pIrpC->pEx != NULL );
+
+
+ //
+ // If we received this packet without a retry, adjust the
+ // send timeout value.
+ //
+
+ if (( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) &&
+ ( pIrpC->PacketType != NCP_BURST )) {
+
+ SHORT NewTimeout;
+
+ NewTimeout = ( pNpScb->SendTimeout + pNpScb->TickCount ) / 2;
+ pNpScb->SendTimeout = MAX( NewTimeout, pNpScb->TickCount + 1 );
+
+ DebugTrace( 0, Dbg, "Successful exchange, new send timeout = %d\n", pNpScb->SendTimeout );
+ }
+
+ //
+ // If the transport didn't indicate all of the data, we'll need
+ // to post a receive IRP.
+ //
+
+#ifdef NWDBG
+ if (( BytesIndicated < BytesAvailable ) ||
+ ( AlwaysAllocateIrp )){
+#else
+ if ( BytesIndicated < BytesAvailable ) {
+#endif
+
+ if ( ( BooleanFlagOn( pIrpC->Flags, IRP_FLAG_BURST_REQUEST ) ) &&
+ ( IsListEmpty( &pIrpC->Specific.Read.PacketList ) ) ) {
+
+ pBurstRsp = (PNCP_BURST_READ_RESPONSE)RspData;
+ BurstStatus = NwBurstResultToNtStatus( pBurstRsp->Result );
+
+ //
+ // If this entire burst failed with an error, we can't
+ // let the receive data routine signal the caller until
+ // the pEx gets called and we exit on the correct paths.
+ //
+
+ if ( !NT_SUCCESS( BurstStatus ) ) {
+
+ DebugTrace( 0, Dbg, "Special burst termination %08lx.\n", BurstStatus );
+ pIrpC->Specific.Read.Status = BurstStatus;
+
+ if ( pNpScb->Sending ) {
+
+ //
+ // If the send hasn't completed yet, we can't accept
+ // the packet because IPX may not have completed back
+ // to us yet!
+ //
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+ DebugTrace(-1, Dbg, "ServerDatagramHandler -> STATUS_DATA_NOT_ACCEPTED (%08lx)\n", BurstStatus );
+ return( STATUS_DATA_NOT_ACCEPTED );
+
+ } else {
+
+ //
+ // Handle this one just like normal, except that we
+ // know it's going to fail in the receive data routine
+ // and we don't want the timeout routine to fire
+ // causing us all sort of grief, so we set OkToReceive
+ // to FALSE.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+ }
+ }
+
+ }
+
+ FreeReceiveIrp( pIrpC ); // Free old Irp if one was allocated
+
+ Status = AllocateReceiveIrp(
+ pIrpC,
+ RspData,
+ BytesAvailable,
+ BytesTaken,
+ pTdiStruct );
+
+ if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
+
+ pNpScb->OkToReceive = FALSE;
+ pNpScb->Receiving = TRUE;
+
+ } else if (!NT_SUCCESS( Status ) ) {
+
+ pIrpC->ReceiveIrp = NULL;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ *IoRequestPacket = pIrpC->ReceiveIrp;
+
+ } else {
+
+ pNpScb->OkToReceive = FALSE;
+
+ //
+ // The transport has indicated all of the data.
+ // If the send has completed, call the pEx routine,
+ // otherwise copy the data to a buffer and let the
+ // send completion routine call the pEx routine.
+ //
+
+ if ( pNpScb->Sending ) {
+ DebugTrace( 0, Dbg, "Received data before send completion\n", 0 );
+
+ Status = CopyIndicatedData(
+ pIrpC,
+ RspData,
+ BytesIndicated,
+ BytesTaken,
+ ReceiveDatagramFlags );
+
+ if (NT_SUCCESS(Status)) {
+ pNpScb->Received = TRUE;
+ pNpScb->Receiving = TRUE;
+ } else {
+ // Ignore this packet
+ pNpScb->OkToReceive = TRUE;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ } else {
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ DebugTrace(+0, Dbg, "Call pIrpC->pEx %x\n", pIrpC->pEx );
+
+ Status = pIrpC->pEx(pIrpC,
+ BytesAvailable,
+ RspData);
+ }
+
+ *BytesTaken = BytesAvailable;
+
+ }
+
+ } else {
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+ Status = STATUS_DATA_NOT_ACCEPTED;
+
+ }
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ DebugTrace(-1, Dbg, "ServerDatagramHandler -> %08lx\n", Status );
+ return( Status );
+
+} // ServerDatagramHandler
+
+NTSTATUS
+CopyIndicatedData(
+ PIRP_CONTEXT pIrpContext,
+ PCHAR ReceiveData,
+ ULONG BytesIndicated,
+ PULONG BytesAccepted,
+ ULONG ReceiveDatagramFlags
+ )
+/*++
+
+Routine Description:
+
+ This routine copies indicated data to a buffer. If the packet is small
+ enough the data is copied to the preallocated receive buffer in the
+ IRP context. If the packet is too long, a new buffer is allocated.
+
+Arguments:
+
+ pIrpContext - A pointer the block of context information for the request
+ in progress.
+
+ ReceiveData - A pointer to the indicated data.
+
+ BytesIndicated - The number of bytes available in the received packet.
+
+ BytesAccepted - Returns the number of bytes accepted by the receive
+ routine.
+
+ ReceiveDatagramFlags - Receive flags given to us by the transport.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ NTSTATUS Status;
+ PMDL ReceiveMdl;
+ PVOID MappedVa;
+ ULONG BytesToCopy;
+ BOOLEAN DeleteMdl = FALSE;
+
+ pIrpContext->ResponseLength = BytesIndicated;
+
+ //
+ // If there is a receive data routine, use it to generate the receive
+ // MDL, otherwise use the default MDL.
+ //
+
+ if ( pIrpContext->ReceiveDataRoutine != NULL ) {
+
+ Status = pIrpContext->ReceiveDataRoutine(
+ pIrpContext,
+ BytesIndicated,
+ BytesAccepted,
+ ReceiveData,
+ &ReceiveMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // We can accept up to the size of a burst read header, plus
+ // 3 bytes of fluff for the unaligned read case.
+ //
+
+ ASSERT( *BytesAccepted <= sizeof(NCP_BURST_READ_RESPONSE) + 3 );
+
+ BytesIndicated -= *BytesAccepted;
+ ReceiveData += *BytesAccepted;
+
+ DeleteMdl = TRUE;
+
+ } else {
+
+ *BytesAccepted = 0;
+ ReceiveMdl = pIrpContext->RxMdl;
+
+ }
+
+ if ( ReceiveMdl != NULL ) {
+
+ while ( BytesIndicated > 0 && ReceiveMdl != NULL ) {
+
+ MappedVa = MmGetSystemAddressForMdl( ReceiveMdl );
+ BytesToCopy = MIN( MmGetMdlByteCount( ReceiveMdl ), BytesIndicated );
+ TdiCopyLookaheadData( MappedVa, ReceiveData, BytesToCopy, ReceiveDatagramFlags );
+
+ ReceiveMdl = ReceiveMdl->Next;
+ BytesIndicated -= BytesToCopy;
+ ReceiveData += BytesToCopy;
+
+ ASSERT( !( BytesIndicated != 0 && ReceiveMdl == NULL ) );
+ }
+
+ if (DeleteMdl) {
+
+ PMDL Mdl = pIrpContext->Specific.Read.PartialMdl;
+ PMDL NextMdl;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ DebugTrace( 0, Dbg, "Freeing MDL %x\n", Mdl );
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ pIrpContext->Specific.Read.PartialMdl = NULL;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+AllocateReceiveIrp(
+ PIRP_CONTEXT pIrpContext,
+ PVOID ReceiveData,
+ ULONG BytesAvailable,
+ PULONG BytesAccepted,
+ PNW_TDI_STRUCT pTdiStruct
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an IRP and if necessary a receive buffer. It
+ then builds an MDL for the buffer and formats the IRP to do a TDI
+ receive.
+
+ BUGBUG - Consider preallocating and queueing for efficiency.
+
+Arguments:
+
+ pIrpContext - A pointer the block of context information for the request
+ in progress.
+
+ ReceiveData - The indicated data.
+
+ BytesAvailable - The number of bytes available in the received packet.
+
+ BytesAccepted - Returns the number of bytes accepted from the packet.
+
+ pTdiStruct - A pointer to the TdiStruct which has indicated the receive.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+ STATUS_MORE_PROCESSING_REQUIRED means we were successful.
+
+--*/
+{
+ PIRP Irp = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ ASSERT( pTdiStruct != NULL );
+
+ Irp = ALLOCATE_IRP( pIrpContext->pNpScb->Server.pDeviceObject->StackSize, FALSE );
+
+ if ( Irp == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto CleanExit;
+ }
+
+ //
+ // If there is no receive data routine for this IRP, the
+ // RxMdl must point to a valid place to put the data.
+ //
+ // If there is a ReceiveDataRoutine it will build an MDL
+ //
+
+ if ( pIrpContext->ReceiveDataRoutine == NULL ) {
+
+ ULONG LengthOfMdl;
+
+ LengthOfMdl = MdlLength( pIrpContext->RxMdl );
+
+ //
+ // If the server sent more data than we can receive, simply
+ // ignore the excess. In particular 3.11 pads long name
+ // response with an excess of junk.
+ //
+
+ if ( BytesAvailable > LengthOfMdl ) {
+ BytesAvailable = LengthOfMdl;
+ }
+
+ Irp->MdlAddress = pIrpContext->RxMdl;
+ *BytesAccepted = 0;
+
+ } else {
+
+ Status = pIrpContext->ReceiveDataRoutine(
+ pIrpContext,
+ BytesAvailable,
+ BytesAccepted,
+ ReceiveData,
+ &Irp->MdlAddress );
+
+ if ( !NT_SUCCESS( Status ) ||
+ Irp->MdlAddress == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto CleanExit;
+
+ }
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_FREE_RECEIVE_MDL );
+
+ }
+
+CleanExit:
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Irp != NULL ) {
+ FREE_IRP( Irp );
+ }
+
+ Irp = NULL;
+ pIrpContext->ReceiveIrp = NULL;
+ Status = STATUS_DATA_NOT_ACCEPTED;
+ return( Status );
+ }
+
+ pIrpContext->ReceiveIrp = Irp;
+ Status = STATUS_MORE_PROCESSING_REQUIRED;
+
+ pIrpContext->ResponseLength = BytesAvailable;
+
+ TdiBuildReceive(
+ Irp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ ReceiveIrpCompletion,
+ pIrpContext,
+ Irp->MdlAddress,
+ 0,
+ BytesAvailable - *BytesAccepted );
+
+ IoSetNextIrpStackLocation( Irp );
+
+ return( Status );
+}
+
+NTSTATUS
+ReceiveIrpCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a recieve IRP completes.
+
+Arguments:
+
+ DeviceObject - Unused.
+
+ Irp - The IRP that completed.
+
+ Context - A pointer the block of context information for the request
+ in progress.
+
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)Context;
+ PIO_STACK_LOCATION IrpSp;
+ PNONPAGED_SCB pNpScb;
+ PMDL Mdl, NextMdl;
+ KIRQL OldIrql;
+
+ ASSERT( Irp == IrpContext->ReceiveIrp );
+
+ pNpScb = IrpContext->pNpScb;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Free the IRP MDL if we allocated one specifically for this IRP.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_FREE_RECEIVE_MDL ) ) {
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ DebugTrace( 0, Dbg, "Freeing MDL %x\n", Mdl );
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ }
+
+ if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) {
+
+ //
+ // Failed to receive the data. Wait for more.
+ //
+
+ pNpScb->OkToReceive = TRUE;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ //
+ // If the send has completed, call the pEx routine,
+ // otherwise copy the data to a buffer and let the
+ // send completion routine call the pEx routine.
+ //
+
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+
+ if ( pNpScb->Sending ) {
+ DebugTrace( 0, Dbg, "Received data before send completion\n", 0 );
+
+ //
+ // Tell send completion to call pEx.
+ //
+
+ pNpScb->Received = TRUE;
+ KeReleaseSpinLock(&pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace(+0, Dbg, "Call pIrpC->pEx %x\n", IrpContext->pEx );
+ IrpContext->pEx(
+ IrpContext,
+ IrpContext->ResponseLength,
+ IrpContext->rsp );
+
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+VOID
+FreeReceiveIrp(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees a IRP that was allocated to do a receive.
+
+Arguments:
+
+ IrpContext - A pointer the block of context information for the request
+ in progress.
+
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ if ( IrpContext->ReceiveIrp == NULL ) {
+ return;
+ }
+
+ FREE_IRP( IrpContext->ReceiveIrp );
+ IrpContext->ReceiveIrp = NULL;
+}
+
+
+NTSTATUS
+WatchDogDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+
+ *IoRequestPacket = NULL;
+
+
+ //
+ // Transport will complete the processing of the request, we don't
+ // want the datagram.
+ //
+
+
+ DebugTrace(+1, Dbg, "WatchDogDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "SourceAddressLength %x\n", SourceAddressLength);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+ DebugTrace(+0, Dbg, "BytesTaken %x\n", *BytesTaken);
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+ DebugTrace(+0, 0, "nwrdr: Invalid Watchdog Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ if ( RspData[1] == NCP_SEARCH_CONTINUE ) {
+ PIRP pIrp;
+ PIRP_CONTEXT pIrpContext;
+
+ pIrp = ALLOCATE_IRP( pNpScb->WatchDog.pDeviceObject->StackSize, FALSE);
+ if (pIrp == NULL) {
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ try {
+ pIrpContext = AllocateIrpContext( pIrp );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_IRP( pIrp );
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+
+ pIrpContext->req[0] = pNpScb->ConnectionNo;
+
+ //
+ // Response 'Y' or connection is valid and its from the right server,
+ // or 'N' if it is not.
+ //
+
+ if (( RspData[0] == pNpScb->ConnectionNo ) &&
+ ( RtlCompareMemory(
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address,
+ &pNpScb->ServerAddress,
+ 8) == 8 ))
+ {
+ LARGE_INTEGER KillTime, Now;
+ BOOL ScbIsOld ;
+
+ //
+ // Check if this is a not-logged-in SCB that has not been used
+ // for while. If it is, answer NO. In attach.c, we dont disconnect
+ // from a nearest server immediately to avoid the re-connect
+ // overheads. This is where we time the sucker out.
+ //
+
+ KeQuerySystemTime( &Now );
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME);
+
+ ScbIsOld = ((pNpScb->State == SCB_STATE_LOGIN_REQUIRED) &&
+ (pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart)) ;
+
+
+ pIrpContext->req[1] = ScbIsOld ? 'N' : 'Y';
+
+ if (ScbIsOld)
+ {
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED ;
+ }
+
+ DebugTrace(-1,Dbg,"WatchDog Response: %s\n", ScbIsOld ? "N" : "Y");
+
+ } else {
+
+ pIrpContext->req[1] = 'N';
+ }
+
+ pIrpContext->TxMdl->ByteCount = 2;
+
+ pIrpContext->ConnectionInformation.UserDataLength = 0;
+ pIrpContext->ConnectionInformation.OptionsLength = sizeof( UCHAR );
+ pIrpContext->ConnectionInformation.Options = &SapPacketType;
+ pIrpContext->ConnectionInformation.RemoteAddressLength = sizeof(TA_IPX_ADDRESS);
+ pIrpContext->ConnectionInformation.RemoteAddress = &pIrpContext->Destination;
+
+ BuildIpxAddress(
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].NetworkAddress,
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].NodeAddress,
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].Socket,
+ &pIrpContext->Destination);
+
+ TdiBuildSendDatagram(
+ pIrpContext->pOriginalIrp,
+ pNpScb->WatchDog.pDeviceObject,
+ pNpScb->WatchDog.pFileObject,
+ &CompletionWatchDogSend,
+ pIrpContext,
+ pIrpContext->TxMdl,
+ MdlLength(pIrpContext->TxMdl),
+ &pIrpContext->ConnectionInformation);
+
+ IoCallDriver(
+ pNpScb->WatchDog.pDeviceObject,
+ pIrpContext->pOriginalIrp );
+ }
+
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+
+ UNREFERENCED_PARAMETER( SourceAddressLength );
+ UNREFERENCED_PARAMETER( BytesIndicated );
+ UNREFERENCED_PARAMETER( BytesAvailable );
+ UNREFERENCED_PARAMETER( BytesTaken );
+ UNREFERENCED_PARAMETER( Tsdu );
+ UNREFERENCED_PARAMETER( OptionsLength );
+ UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+CompletionWatchDogSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine does not complete the Irp. It is used to signal to a
+ synchronous part of the driver that it can proceed.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+
+ PIRP_CONTEXT pIrpC = (PIRP_CONTEXT) Context;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "CompletionWatchDogSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( -1, Dbg, "pIrpC %X\n", pIrpC);
+
+ FREE_IRP( pIrpC->pOriginalIrp );
+
+ pIrpC->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
+
+ FreeIrpContext( pIrpC );
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+
+NTSTATUS
+SendDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+ PIRP_CONTEXT pIrpContext;
+ PLIST_ENTRY listEntry;
+ PIRP Irp;
+
+ *IoRequestPacket = NULL;
+
+ DebugTrace(0, Dbg, "SendDatagramHandler\n", 0);
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ //
+ // Transport will complete the processing of the request, we don't
+ // want the datagram.
+ //
+
+ DebugTrace(+1, Dbg, "SendDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "SourceAddressLength %x\n", SourceAddressLength);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+ DebugTrace(+0, Dbg, "BytesTaken %x\n", *BytesTaken);
+
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+ DebugTrace(+0, Dbg, "nwrdr: Invalid SendDatagram Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ if (RspData[1] == BROADCAST_MESSAGE_WAITING ) {
+
+ //
+ // Broadcast message waiting. If the scavenger
+ // isn't running, it's safe to go get it.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ if ( WorkerRunning ) {
+
+ //
+ // The scavenger is running, we can't pick up this
+ // message until the scavenger is done!
+ //
+
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Delaying get message for scavenger.\n", 0 );
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ } else {
+
+ //
+ // Make sure the scavenger doesn't start.
+ //
+
+ WorkerRunning = TRUE;
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ listEntry = ExInterlockedRemoveHeadList(
+ &NwGetMessageList,
+ &NwMessageSpinLock );
+
+ if ( listEntry != NULL ) {
+
+ pIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ //
+ // Clear the cancel routine for this IRP.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ pIrpContext->PostProcessRoutine = FspGetMessage;
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pNpScb->pScb;
+
+ NwPostToFsp( pIrpContext, TRUE );
+
+ } else {
+
+ WorkerRunning = FALSE;
+ }
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+
+ UNREFERENCED_PARAMETER( SourceAddressLength );
+ UNREFERENCED_PARAMETER( BytesIndicated );
+ UNREFERENCED_PARAMETER( BytesAvailable );
+ UNREFERENCED_PARAMETER( BytesTaken );
+ UNREFERENCED_PARAMETER( Tsdu );
+ UNREFERENCED_PARAMETER( OptionsLength );
+ UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+FspGetMessage(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine continues process a broadcast message waiting message.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ UNICODE_STRING Message;
+ NTSTATUS Status;
+ PNWR_SERVER_MESSAGE ServerMessage;
+ PUNICODE_STRING ServerName;
+ ULONG MessageLength;
+ int i;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ //
+ // The Scb may be being deleted so carefully walk the list and reference it if
+ // we find it.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if (pNpScb == IrpContext->pNpScb ) {
+
+ NwReferenceScb( pNpScb );
+
+ break;
+ }
+
+ ScbQueueEntry = ScbQueueEntry->Flink;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ if (pNpScb != IrpContext->pNpScb ) {
+
+ //
+ // Server deleted. Its easiest to continue processing the IrpContext
+ // with an error than try to recover it and return it to the queue.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ //
+ // If the message is telling us that the server is going down then don't
+ // work too hard trying to get the message. The server is persistent with
+ // respect to other messages so we'll come through here again when the
+ // problem has been resolved.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( UP_LEVEL_SERVER( IrpContext->pScb ) ) {
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_MESSAGE_FUNCTION, NCP_GET_ENTIRE_MESSAGE );
+ } else {
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_MESSAGE_FUNCTION, NCP_GET_MESSAGE );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ ServerMessage = (PNWR_SERVER_MESSAGE)IrpContext->Specific.FileSystemControl.Buffer;
+ MessageLength = IrpContext->Specific.FileSystemControl.Length;
+
+ ServerName = &IrpContext->pNpScb->ServerName;
+ if ( ServerName->Length + FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) > MessageLength ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+
+ } else {
+
+ //
+ // Copy the server name to the output buffer.
+ //
+
+ ServerMessage->MessageOffset =
+ ServerName->Length +
+ FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) +
+ sizeof(WCHAR);
+
+ RtlMoveMemory(
+ ServerMessage->Server,
+ ServerName->Buffer,
+ ServerName->Length );
+
+ ServerMessage->Server[ ServerName->Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ //
+ // Copy the message to the user's buffer.
+ //
+
+ Message.Buffer = &ServerMessage->Server[ ServerName->Length / sizeof(WCHAR) ] + 1;
+ Message.MaximumLength = (USHORT)( MessageLength - ( ServerName->Length + FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) ) );
+
+ if ( NT_SUCCESS( Status) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "NP",
+ &Message );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ //
+ // Strip the trailing spaces and append a NUL terminator to the message.
+ //
+
+ for ( i = Message.Length / sizeof(WCHAR) - 1; i >= 0 ; i-- ) {
+ if ( Message.Buffer[ i ] != L' ') {
+ Message.Length = (i + 1) * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ if ( Message.Length > 0 ) {
+ Message.Buffer[ Message.Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ IrpContext->pOriginalIrp->IoStatus.Information =
+ ServerName->Length +
+ FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) +
+ Message.Length + sizeof(WCHAR);
+
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+}
+
+
+NTSTATUS
+_cdecl
+ExchangeWithWait(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a NCP packet and waits for the response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ pEX, Context, f - See _Exchange
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ va_list Arguments;
+
+ PAGED_CODE();
+
+ //KeResetEvent( &pIrpContext->Event );
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ va_end( Arguments );
+
+ Status = PrepareAndSendPacket( pIrpContext );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = KeWaitForSingleObject(
+ &pIrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = pIrpContext->pOriginalIrp->IoStatus.Status;
+
+ if ( NT_SUCCESS( Status ) &&
+ pIrpContext->PacketType != SAP_BROADCAST ) {
+ Status = NwErrorToNtStatus( pIrpContext->ResponseParameters.Error );
+ }
+
+ return( Status );
+}
+
+BOOLEAN
+VerifyResponse(
+ PIRP_CONTEXT pIrpContext,
+ PVOID Response
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies that a received response is the expected
+ response for the current request.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ Response - A pointer to the buffer containing the response.
+
+Return Value:
+
+ TRUE - This is a valid response.
+ FALSE - This is an invalid response.
+
+--*/
+
+{
+ PNCP_RESPONSE pNcpResponse;
+ PNONPAGED_SCB pNpScb;
+
+ pNcpResponse = (PNCP_RESPONSE)Response;
+ pNpScb = pIrpContext->pNpScb;
+
+ if ( pNcpResponse->NcpHeader.ConnectionIdLow != pNpScb->ConnectionNo ) {
+ DebugTrace(+0, Dbg, "VerifyResponse, bad connection number\n", 0);
+
+ return( FALSE );
+ }
+
+ if ( pNcpResponse->NcpHeader.SequenceNumber != pNpScb->SequenceNo ) {
+ DebugTrace(+1, Dbg, "VerifyResponse, bad sequence number %x\n", 0);
+ DebugTrace(+0, Dbg, " pNcpResponse->NcpHeader.SequenceNumber %x\n",
+ pNcpResponse->NcpHeader.SequenceNumber);
+ DebugTrace(-1, Dbg, " pNpScb->SequenceNo %x\n", pNpScb->SequenceNo );
+
+ return( FALSE );
+ }
+
+ return( TRUE );
+}
+
+VOID
+ScheduleReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine schedules an a reconnect attempt, and then resubmits
+ our request if the reconnect was successful.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWORK_QUEUE_ITEM WorkItem;
+
+ WorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
+
+ if ( WorkItem == NULL ) {
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ }
+
+ pIrpContext->pWorkItem = WorkItem;
+ ExInitializeWorkItem( WorkItem, ReconnectRetry, pIrpContext );
+ ExQueueWorkItem( WorkItem, DelayedWorkQueue );
+
+ return;
+}
+
+VOID
+ReconnectRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to reconnect to a disconnected server. If it
+ is successful it resubmits an existing request.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP_CONTEXT pNewIrpContext;
+ PSCB pScb, pNewScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ Stats.Reconnects++;
+
+ if ( pScb == NULL ) {
+ pScb = pNpScb->pScb;
+ pIrpContext->pScb = pScb;
+ }
+
+ //
+ // Free the work item
+ //
+
+ FREE_POOL( pIrpContext->pWorkItem );
+
+ //
+ // Allocate a temporary IRP context to use to reconnect to the server
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ return;
+ }
+
+ pNewIrpContext->Specific.Create.UserUid = pScb->UserUid;
+ pNewIrpContext->pNpScb = pNpScb;
+ pNewIrpContext->pScb = pScb;
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->SequenceNo = 0;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Now insert this new IrpContext to the head of the SCB queue for
+ // processing. We can get away with this because we own the IRP context
+ // currently at the front of the queue. With the RECONNECT_ATTEMPT
+ // flag set, ConnectScb() will not remove us from the head of the queue.
+ //
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ pNewScb = pNpScb->pScb;
+
+ Status = ConnectScb( &pNewScb,
+ pNewIrpContext,
+ &pNpScb->ServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Couldn't reconnect. Free the extra IRP context, complete the
+ // original request with an error.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwFreeExtraIrpContext( pNewIrpContext );
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ return;
+ }
+
+ ASSERT( pNewScb == pScb );
+
+ //
+ // Try to reconnect the VCBs.
+ //
+
+ NwReopenVcbHandlesForScb( pNewIrpContext, pScb );
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ //
+ // Resubmit the original request, with a new sequence number. Note that
+ // it's back at the front of the queue, but no longer reconnectable.
+ //
+
+ pIrpContext->req[2] = pNpScb->SequenceNo;
+ pIrpContext->req[3] = pNpScb->ConnectionNo;
+ pIrpContext->req[5] = pNpScb->ConnectionNoHigh;
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+ SendNow( pIrpContext );
+
+ return;
+}
+
+
+NTSTATUS
+NewRouteRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to establish a new route to a non-responding server.
+ If it is successful it resubmits the request in progress.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ LARGE_INTEGER CurrentTime = {0, 0};
+
+ PAGED_CODE();
+
+ //
+ // Don't bother to re-rip if we are shutting down.
+ //
+
+ if ( NwRcb.State != RCB_STATE_SHUTDOWN ) {
+ Status = GetNewRoute( pIrpContext );
+ } else {
+ Status = STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Ask the transport to establish a new route to the server.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Attempt to get new route failed, fail the current request.
+ //
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ if ( pNpScb != &NwPermanentNpScb ) {
+
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ pNpScb->ServerName.Buffer );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+
+ }
+
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ } else {
+
+ //
+ // Got a new route, resubmit the request. Allow retries
+ // with the new route.
+ //
+
+ pIrpContext->pNpScb->RetryCount = DefaultRetryCount / 2;
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+ SendNow( pIrpContext );
+ }
+
+ //
+ // Return STATUS_PENDING so that the FSP dispatcher doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+
+NTSTATUS
+NewRouteBurstRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to establish a new route to a non-responding server.
+ If it is successful it resubmits the request in progress.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT pNewIrpContext;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ BOOLEAN LIPNegotiated ;
+ LARGE_INTEGER CurrentTime = {0, 0};
+
+ PAGED_CODE();
+
+ //
+ // Don't bother to re-rip if we are shutting down.
+ //
+
+ if ( NwRcb.State == RCB_STATE_SHUTDOWN ) {
+ return( STATUS_REMOTE_NOT_LISTENING );
+ }
+
+ //
+ // Ask the transport to establish a new route to the server.
+ //
+
+ Status = GetNewRoute( pIrpContext );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // If this is a burst write, we must first complete the write
+ // request (there is no way to tell the server to abandon the write).
+ //
+ // Set packet size down to 512 to guarantee that the packets will be
+ // forwarded, and resend the burst data. Queue the new IRP context
+ // behind the burst write, so that we can establish a new burst
+ // connection.
+ //
+ // Note that ResubmitBurstWrite may complete the request and
+ // free the IrpContext.
+ //
+
+ pNpScb->RetryCount = DefaultRetryCount / 2;
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_BURST_WRITE ) ) {
+
+ Status = ResubmitBurstWrite( pIrpContext );
+
+ } else {
+
+ //
+ // Allocate a temporary IRP context to use to reconnect to the server
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ pNewIrpContext->Specific.Create.UserUid = pIrpContext->Specific.Create.UserUid;
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ //
+ // Since we're doing this from a worker thread, we can't
+ // let the dpc timer schedule _another_ worker thread
+ // request if this also times out or we may deadlock
+ // the delayed work queue.
+ //
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ pNewIrpContext->pNpScb = pNpScb;
+
+ }
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Insert this new IrpContext to the head of
+ // the SCB queue for processing. We can get away with this
+ // because we own the IRP context currently at the front of
+ // the queue.
+ //
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ //
+ // Now prepare to resend the burst read.
+ //
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ //
+ // Renegotiate the burst connection, this will automatically re-sync
+ // the burst connection.
+ //
+ // BUGBUG: We lose sizeof( NCP_BURST_WRITE_REQUEST ) each time
+ // we do this right now.
+ //
+
+ NegotiateBurstMode( pNewIrpContext, pNpScb, &LIPNegotiated );
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ ASSERT( pNpScb->Requests.Flink == &pNewIrpContext->NextRequest );
+
+ ExInterlockedRemoveHeadList(
+ &pNpScb->Requests,
+ &pNpScb->NpScbSpinLock );
+
+ ClearFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ //
+ // Got a new route, resubmit the request
+ //
+
+ Status = ResubmitBurstRead( pIrpContext );
+ }
+ }
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Attempt to get new route failed, fail the current request.
+ //
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ if ( pNpScb != &NwPermanentNpScb ) {
+
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ pNpScb->ServerName.Buffer );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+
+ }
+
+ }
+ }
+
+ //
+ // Return STATUS_PENDING so that the FSP dispatcher doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+NTSTATUS
+FspProcessServerDown(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine process a response with the server shutdown bit set.
+ It close all open handles for the server, and puts the server in
+ the attaching state.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ STATUS_PENDING.
+
+--*/
+{
+ KIRQL OldIrql;
+
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+
+ //
+ // Avoid the Scb from disappearing under us.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Move the IrpContext from the front of the queue just in-case it
+ // owns the Rcb.
+ //
+
+ KeAcquireSpinLock( &IrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ if ( IrpContext->pNpScb->Sending ) {
+
+ //
+ // Let send completion call the pEx routine
+ //
+
+ IrpContext->pNpScb->Received = TRUE;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+
+ IrpContext->pNpScb->Receiving = FALSE;
+ IrpContext->pNpScb->Received = FALSE;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ //
+ // Now call the callback routine.
+ //
+
+ IrpContext->pEx(
+ IrpContext,
+ IrpContext->ResponseLength,
+ IrpContext->rsp );
+
+ }
+
+ //
+ // Close all active handles for this server.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ NwInvalidateAllHandlesForScb( pNpScb->pScb );
+ NwReleaseRcb( &NwRcb );
+
+ NwDereferenceScb( pNpScb );
+
+ //
+ // Return STATUS_PENDING so that the FSP process doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+
+VOID
+NwProcessSendBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after an unsuccessful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+ MissingFragmentCount - A measure of how many chunks were lost.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Burst failure, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwSendDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ NpScb->NwBadSendDelay = NpScb->NwSendDelay;
+
+ //
+ // Add to the send delay. Never let it go above 5000ms.
+ //
+
+ temp = NpScb->NwGoodSendDelay - NpScb->NwBadSendDelay;
+
+ if (temp >= 0) {
+ NpScb->NwSendDelay += temp + 2;
+ } else {
+ NpScb->NwSendDelay += -temp + 2;
+ }
+
+ if ( NpScb->NwSendDelay > NpScb->NwMaxSendDelay ) {
+
+ NpScb->NwSendDelay = NpScb->NwMaxSendDelay;
+
+ //
+ // If we have slowed down a lot then it might be that the server or a
+ // bridge only has a small buffer on its NIC. If this is the case then
+ // rather than sending a big burst with long even gaps between the
+ // packets, we should try to send a burst the size of the buffer.
+ //
+
+ if ( !DontShrink ) {
+
+ if (((NpScb->MaxSendSize - 1) / NpScb->MaxPacketSize) > 2 ) {
+
+ // Round down to the next packet
+
+ NpScb->MaxSendSize = ((NpScb->MaxSendSize - 1) / NpScb->MaxPacketSize) * NpScb->MaxPacketSize;
+
+ //
+ // Adjust SendDelay below threshold to see if things improve before
+ // we shrink the size again.
+ //
+
+ NpScb->NwSendDelay = NpScb->NwGoodSendDelay = NpScb->NwBadSendDelay = MinSendDelay;
+
+ } else {
+
+ //
+ // We reached the minimum size with the maximum delay. Give up on burst.
+ //
+
+ NpScb->SendBurstModeEnabled = FALSE;
+
+ }
+
+ }
+ }
+
+ NpScb->NtSendDelay.QuadPart = NpScb->NwSendDelay * -1000 ;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Send Delay = %d\n", NpScb->NwSendDelay );
+
+ NpScb->SendBurstSuccessCount = 0;
+
+}
+
+
+VOID
+NwProcessReceiveBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after an unsuccessful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+ MissingFragmentCount - A measure of how many chunks were lost.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace(+0, DEBUG_TRACE_LIP, "Burst failure, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwReceiveDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ NpScb->NwBadReceiveDelay = NpScb->NwReceiveDelay;
+
+ //
+ // Add to the Receive delay. Never let it go above 5000ms.
+ //
+
+ temp = NpScb->NwGoodReceiveDelay - NpScb->NwBadReceiveDelay;
+
+ if (temp >= 0) {
+ NpScb->NwReceiveDelay += temp + 2;
+ } else {
+ NpScb->NwReceiveDelay += -temp + 2;
+ }
+
+
+ if ( NpScb->NwReceiveDelay > NpScb->NwMaxReceiveDelay ) {
+
+ NpScb->NwReceiveDelay = MaxReceiveDelay;
+
+ //
+ // If we have slowed down a lot then it might be that the server or a
+ // bridge only has a small buffer on its NIC. If this is the case then
+ // rather than Receiveing a big burst with long even gaps between the
+ // packets, we should try to Receive a burst the size of the buffer.
+ //
+
+ if ( !DontShrink ) {
+
+ if (((NpScb->MaxReceiveSize - 1) / NpScb->MaxPacketSize) > 2 ) {
+
+ // Round down to the next packet
+
+ NpScb->MaxReceiveSize = ((NpScb->MaxReceiveSize - 1) / NpScb->MaxPacketSize) * NpScb->MaxPacketSize;
+
+ //
+ // Adjust ReceiveDelay below threshold to see if things improve before
+ // we shrink the size again.
+ //
+
+ NpScb->NwReceiveDelay = NpScb->NwGoodReceiveDelay = NpScb->NwBadReceiveDelay = MinReceiveDelay;
+
+ } else {
+
+ //
+ // We reached the minimum size with the maximum delay. Give up on burst.
+ //
+
+ NpScb->ReceiveBurstModeEnabled = FALSE;
+
+ }
+
+ }
+
+ }
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Receive Delay = %d\n", NpScb->NwReceiveDelay );
+}
+
+
+VOID
+NwProcessSendBurstSuccess(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after a successful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has completed the burst.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Successful burst, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwSendDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ if ( NpScb->SendBurstSuccessCount > BurstSuccessCount ) {
+
+ if (NpScb->NwSendDelay != MinSendDelay ) {
+
+ NpScb->NwGoodSendDelay = NpScb->NwSendDelay;
+
+ temp = NpScb->NwGoodSendDelay - NpScb->NwBadSendDelay;
+
+ if (temp >= 0) {
+ NpScb->NwSendDelay -= 1 + temp;
+ } else {
+ NpScb->NwSendDelay -= 1 - temp;
+ }
+
+ if (NpScb->NwSendDelay < MinSendDelay ) {
+
+ NpScb->NwSendDelay = MinSendDelay;
+
+ }
+
+ NpScb->NtSendDelay.QuadPart = NpScb->NwSendDelay * -1000;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Send Delay = %d\n", NpScb->NwSendDelay );
+
+ //
+ // Start monitoring success at the new rate.
+ //
+
+ NpScb->SendBurstSuccessCount = 0;
+
+ } else if ( NpScb->SendBurstSuccessCount > BurstSuccessCount2 ) {
+
+ //
+ // We may have had a really bad patch causing BadSendDelay to be very big.
+ // If we leave it at its current value then at the first sign of trouble
+ // we will make SendDelay very big
+ //
+
+ NpScb->NwGoodSendDelay = NpScb->NwBadSendDelay = NpScb->NwSendDelay;
+
+ //
+ // Is it time to increase the number of packets in the burst?
+ // AllowGrowth == 0 to be the same as the VLM client.
+ //
+
+ if (( AllowGrowth ) &&
+ ( NpScb->NwSendDelay <= MinSendDelay ) &&
+ ( NpScb->MaxSendSize < NwMaxSendSize)) {
+
+ NpScb->MaxSendSize += NpScb->MaxPacketSize;
+
+
+ if ( NpScb->MaxSendSize > NwMaxSendSize) {
+
+ NpScb->MaxSendSize = NwMaxSendSize;
+
+ }
+ }
+
+ NpScb->SendBurstSuccessCount = 0;
+
+ } else {
+
+ NpScb->SendBurstSuccessCount++;
+
+ }
+
+
+ } else {
+
+ NpScb->SendBurstSuccessCount++;
+
+ }
+
+}
+
+
+VOID
+NwProcessReceiveBurstSuccess(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after a successful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has completed the burst.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Successful burst, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwReceiveDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ if ( NpScb->ReceiveBurstSuccessCount > BurstSuccessCount ) {
+
+ //
+ // Once the vlm client reaches the Maximum delay it does not
+ // shrink again.
+ //
+
+ if ( NpScb->NwReceiveDelay != MinReceiveDelay ) {
+
+ NpScb->NwGoodReceiveDelay = NpScb->NwReceiveDelay;
+
+ temp = NpScb->NwGoodReceiveDelay - NpScb->NwBadReceiveDelay;
+
+ if (temp >= 0) {
+ NpScb->NwReceiveDelay -= 1 + temp;
+ } else {
+ NpScb->NwReceiveDelay -= 1 - temp;
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Receive Delay = %d\n", NpScb->NwReceiveDelay );
+
+
+ if (NpScb->NwReceiveDelay < MinReceiveDelay ) {
+ NpScb->NwReceiveDelay = MinReceiveDelay;
+
+ }
+
+ //
+ // Start monitoring success at the new rate.
+ //
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ } else if ( NpScb->ReceiveBurstSuccessCount > BurstSuccessCount2 ) {
+
+ //
+ // We may have had a really bad patch causing BadReceiveDelay to be very big.
+ // If we leave it at its current value then at the first sign of trouble
+ // we will make ReceiveDelay very big
+ //
+
+ NpScb->NwGoodReceiveDelay = NpScb->NwBadReceiveDelay = NpScb->NwReceiveDelay;
+
+
+ //
+ // Is it time to increase the number of packets in the burst?
+ //
+
+ if (( AllowGrowth ) &&
+ ( NpScb->NwReceiveDelay <= MinReceiveDelay ) &&
+ ( NpScb->MaxReceiveSize < NwMaxReceiveSize)) {
+
+ NpScb->MaxReceiveSize += NpScb->MaxPacketSize;
+
+
+ if ( NpScb->MaxReceiveSize > NwMaxReceiveSize) {
+
+ NpScb->MaxReceiveSize = NwMaxReceiveSize;
+
+ }
+ }
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ } else {
+
+ NpScb->ReceiveBurstSuccessCount++;
+
+ }
+
+ } else {
+
+ NpScb->ReceiveBurstSuccessCount++;
+
+ }
+
+}
+
+
+VOID
+NwProcessPositiveAck(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine processes a positive acknowledgement.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DebugTrace( 0, Dbg, "Positive ACK, NpScb = %X\n", NpScb );
+
+ NpScb->TotalWaitTime += DefaultRetryCount;
+
+ //
+ // If we have not waited longer than the absolute total, keep waiting.
+ // If we have waited too long, let ourselves timeout.
+ //
+ // If NwAbsoluteTotalWaitTime is 0, then we are prepared to wait forever.
+ //
+
+ if ( NpScb->TotalWaitTime < NwAbsoluteTotalWaitTime ||
+ NwAbsoluteTotalWaitTime == 0) {
+
+ NpScb->RetryCount = DefaultRetryCount;
+
+ } else {
+ DebugTrace( 0, Dbg, "Request exceeds absolute total wait time\n", 0 );
+ }
+}
+
+
diff --git a/private/nw/rdr/exchange.h b/private/nw/rdr/exchange.h
new file mode 100644
index 000000000..dcfb70060
--- /dev/null
+++ b/private/nw/rdr/exchange.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Exchange.h
+
+Abstract:
+
+ This module defines all of the objects exported by exchange.c in the
+ NetWare redirector.
+
+Author:
+
+ Colin Watson [ColinW] 1-Feb-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWEXCHANGE_
+#define _NWEXCHANGE_
+
+//
+// Define the prototype for post_exchange routines.
+//
+
+struct _IRP_CONTEXT;
+struct _NONPAGED_SCB;
+
+//
+// Prototype for the exchange routine which starts an NCB transmission
+//
+
+NTSTATUS
+_cdecl
+Exchange
+(
+ struct _IRP_CONTEXT* pIrpC,
+ PEX pEx,
+ char* f,
+ ...
+);
+
+//
+// Prototype of routine that can be used to process the response packet
+//
+
+NTSTATUS
+_cdecl
+ExchangeReply(
+ IN PUCHAR RspData,
+ IN ULONG BytesIndicated,
+ char* f,
+ ... // format specific parameters
+ );
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ );
+
+VOID
+KickQueue(
+ struct _NONPAGED_SCB* pNpScb
+ );
+
+NTSTATUS
+ServerDatagramHandler(
+ IN PVOID TdiEventContext, // the event context - pNpScb
+ IN int SourceAddressLength, // length of the originator of the datagram
+ IN PVOID SourceAddress, // string describing the originator of the datagram
+ IN int OptionsLength, // options for the receive
+ IN PVOID Options, //
+ IN ULONG ReceiveDatagramFlags, //
+ IN ULONG BytesIndicated, // number of bytes this indication
+ IN ULONG BytesAvailable, // number of bytes in complete Tsdu
+ OUT ULONG *BytesTaken, // number of bytes used
+ IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
+ OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+ );
+
+NTSTATUS
+WatchDogDatagramHandler(
+ IN PVOID TdiEventContext, // the event context - pNpScb
+ IN int SourceAddressLength, // length of the originator of the datagram
+ IN PVOID SourceAddress, // string describing the originator of the datagram
+ IN int OptionsLength, // options for the receive
+ IN PVOID Options, //
+ IN ULONG ReceiveDatagramFlags, //
+ IN ULONG BytesIndicated, // number of bytes this indication
+ IN ULONG BytesAvailable, // number of bytes in complete Tsdu
+ OUT ULONG *BytesTaken, // number of bytes used
+ IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
+ OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+ );
+
+NTSTATUS
+SendDatagramHandler(
+ IN PVOID TdiEventContext, // the event context - pNpScb
+ IN int SourceAddressLength, // length of the originator of the datagram
+ IN PVOID SourceAddress, // string describing the originator of the datagram
+ IN int OptionsLength, // options for the receive
+ IN PVOID Options, //
+ IN ULONG ReceiveDatagramFlags, //
+ IN ULONG BytesIndicated, // number of bytes this indication
+ IN ULONG BytesAvailable, // number of bytes in complete Tsdu
+ OUT ULONG *BytesTaken, // number of bytes used
+ IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
+ OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+ );
+
+#endif // _NWEXCHANGE_
diff --git a/private/nw/rdr/fileinfo.c b/private/nw/rdr/fileinfo.c
new file mode 100644
index 000000000..e9453a317
--- /dev/null
+++ b/private/nw/rdr/fileinfo.c
@@ -0,0 +1,2905 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileinfo.c
+
+Abstract:
+
+ This module implements the get / set file information routines for
+ Netware Redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 4-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FILEINFO)
+
+//
+// local procedure prototypes
+//
+
+NTSTATUS
+NwCommonQueryInformation (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NwCommonSetInformation (
+ IN PIRP_CONTEXT pIrpContet
+ );
+
+NTSTATUS
+NwQueryBasicInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_BASIC_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryStandardInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_STANDARD_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryInternalInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_INTERNAL_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryEaInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_EA_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryNameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_NAME_INFORMATION Buffer,
+ IN OUT PULONG Length
+ );
+
+NTSTATUS
+NwQueryPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetBasicInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_BASIC_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetDispositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_DISPOSITION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetRenameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_RENAME_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetAllocationInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_ALLOCATION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetEndOfFileInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_END_OF_FILE_INFORMATION Buffer
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdQueryInformation )
+#pragma alloc_text( PAGE, NwFsdSetInformation )
+#pragma alloc_text( PAGE, NwCommonQueryInformation )
+#pragma alloc_text( PAGE, NwCommonSetInformation )
+#pragma alloc_text( PAGE, NwQueryStandardInfo )
+#pragma alloc_text( PAGE, NwQueryInternalInfo )
+#pragma alloc_text( PAGE, NwQueryEaInfo )
+#pragma alloc_text( PAGE, NwQueryNameInfo )
+#pragma alloc_text( PAGE, NwQueryPositionInfo )
+#pragma alloc_text( PAGE, NwSetBasicInfo )
+#pragma alloc_text( PAGE, NwSetDispositionInfo )
+#pragma alloc_text( PAGE, NwDeleteFile )
+#pragma alloc_text( PAGE, NwSetRenameInfo )
+#pragma alloc_text( PAGE, NwSetPositionInfo )
+#pragma alloc_text( PAGE, NwSetAllocationInfo )
+#pragma alloc_text( PAGE, NwSetEndOfFileInfo )
+#pragma alloc_text( PAGE, OccurenceCount )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwQueryBasicInfo )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdQueryInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtQueryInformationFile API
+ calls.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object to use.
+
+ Irp - Supplies a pointer to the Irp to process.
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdQueryInformation\n", 0);
+
+ //
+ // Call the common query information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonQueryInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdQueryInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwFsdSetInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtSetInformationFile API
+ calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object to use.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdSetInformation\n", 0);
+
+ //
+ // Call the common Set Information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonSetInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdSetInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonQueryInformation (
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This is the common routine for querying information on a file.
+
+Arguments:
+
+ pIrpContext - Supplies Irp context information.
+
+Return Value:
+
+ NTSTATUS - the return status for the operation.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ ULONG length;
+ FILE_INFORMATION_CLASS fileInformationClass;
+ PVOID buffer;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+
+ PVOID fsContext, fsContext2;
+
+ PFILE_ALL_INFORMATION AllInfo;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonQueryInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
+ DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.QueryFile.FileInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Find out who are.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonQueryInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this the user is querying an ICB.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_ICB:
+
+ icb = (PICB)fsContext2;
+ break;
+
+ default: // This is an illegal file object to query
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+
+ DebugTrace(-1, Dbg, "NwCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pIrpContext->Icb = icb;
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ length = irpSp->Parameters.QueryFile.Length;
+ fileInformationClass = irpSp->Parameters.QueryFile.FileInformationClass;
+ buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Now acquire shared access to the FCB
+ //
+
+ fcb = icb->SuperType.Fcb;
+
+ try {
+
+ NwVerifyIcbSpecial( icb );
+
+ //
+ // Based on the information class we'll do different actions. Each
+ // of the procedure that we're calling fill up as much of the
+ // buffer as possible and return the remaining length, and status
+ // This is done so that we can use them to build up the
+ // FileAllInformation request. These procedures do not complete the
+ // IRP, instead this procedure must complete the IRP.
+ //
+
+ status = STATUS_SUCCESS;
+
+ switch (fileInformationClass) {
+
+ case FileAllInformation:
+
+ AllInfo = buffer;
+
+ //
+ // First call all the Query Info handlers we can call
+ // synchronously.
+ //
+
+ NwQueryInternalInfo( pIrpContext, icb, &AllInfo->InternalInformation );
+ NwQueryEaInfo( pIrpContext, &AllInfo->EaInformation );
+ NwQueryPositionInfo( pIrpContext, icb, &AllInfo->PositionInformation );
+
+ length -= FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation );
+
+ status = NwQueryNameInfo( pIrpContext, icb, &AllInfo->NameInformation, &length );
+
+ if ( !NT_ERROR( status ) ) {
+ status = NwQueryStandardInfo( pIrpContext, icb, &AllInfo->StandardInformation );
+ }
+
+ if ( !NT_ERROR( status ) ) {
+ status = NwQueryBasicInfo( pIrpContext, icb, &AllInfo->BasicInformation );
+ }
+
+ break;
+
+
+ case FileBasicInformation:
+
+ length -= sizeof( FILE_BASIC_INFORMATION );
+ status = NwQueryBasicInfo( pIrpContext, icb, buffer );
+
+ break;
+
+ case FileStandardInformation:
+
+ //
+ // We will handle this call for information asynchronously.
+ // The callback routine will fill in the missing data, and
+ // complete the IRP.
+ //
+ // Remember the buffer length, and status to return.
+ //
+
+ length -= sizeof( FILE_STANDARD_INFORMATION );
+ status = NwQueryStandardInfo( pIrpContext, icb, buffer );
+ break;
+
+ case FileInternalInformation:
+
+ status = NwQueryInternalInfo( pIrpContext, icb, buffer );
+ length -= sizeof( FILE_INTERNAL_INFORMATION );
+ break;
+
+ case FileEaInformation:
+
+ status = NwQueryEaInfo( pIrpContext, buffer );
+ length -= sizeof( FILE_EA_INFORMATION );
+ break;
+
+ case FilePositionInformation:
+
+ status = NwQueryPositionInfo( pIrpContext, icb, buffer );
+ length -= sizeof( FILE_POSITION_INFORMATION );
+ break;
+
+ case FileNameInformation:
+
+ status = NwQueryNameInfo( pIrpContext, icb, buffer, &length );
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // Set the information field to the number of bytes actually
+ // filled in and then complete the request. (This is
+ // irrelavent if the Query worker function returned
+ // STATUS_PENDING).
+ //
+
+ if ( status != STATUS_PENDING ) {
+ Irp->IoStatus.Information =
+ irpSp->Parameters.QueryFile.Length - length;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonQueryInformation -> %08lx\n", status );
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonSetInformation (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This is the common routine for setting information on a file.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ ULONG length;
+ FILE_INFORMATION_CLASS fileInformationClass;
+ PVOID buffer;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ //
+ // Get the current Irp stack location.
+ //
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ DebugTrace(+1, Dbg, "NwCommonSetInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.SetFile.Length);
+ DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.SetFile.FileInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Get a pointer to the FCB and ensure that this is a server side
+ // handler to a file.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb )) == NTC_UNDEFINED ) {
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonSetInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this the user is querying an ICB.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_ICB:
+
+ fcb = icb->SuperType.Fcb;
+ break;
+
+ default: // This is an illegal file object to query
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+
+ DebugTrace(-1, Dbg, "NwCommonSetInformation -> STATUS_INVALID_PARAMETER\n", 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ IrpContext->Icb = icb;
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ length = irpSp->Parameters.SetFile.Length;
+ fileInformationClass = irpSp->Parameters.SetFile.FileInformationClass;
+ buffer = irp->AssociatedIrp.SystemBuffer;
+
+ try {
+
+ NwVerifyIcb( icb );
+
+ //
+ // Based on the information class we'll do different actions. Each
+ // procedure that we're calling will complete the request.
+ //
+
+ switch (fileInformationClass) {
+
+ case FileBasicInformation:
+
+ status = NwSetBasicInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileDispositionInformation:
+
+ status = NwSetDispositionInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileRenameInformation:
+
+ status = NwSetRenameInfo( IrpContext, icb, buffer );
+ break;
+
+ case FilePositionInformation:
+
+ status = NwSetPositionInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileLinkInformation:
+
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ case FileAllocationInformation:
+
+ status = NwSetAllocationInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileEndOfFileInformation:
+
+ status = NwSetEndOfFileInfo( IrpContext, icb, buffer );
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonSetInformation -> %08lx\n", status);
+ }
+
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryBasicInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ OUT PFILE_BASIC_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine performs the query basic information operation.
+ This routine cannot be paged, it is called from QueryStandardInfoCallback.
+
+Arguments:
+
+ Icb - Supplies a pointer the ICB for the file being querying.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PFCB Fcb;
+ NTSTATUS Status;
+ ULONG Attributes;
+ USHORT CreationDate;
+ USHORT CreationTime = DEFAULT_TIME;
+ USHORT LastAccessDate;
+ USHORT LastModifiedDate;
+ USHORT LastModifiedTime;
+ BOOLEAN FirstTime = TRUE;
+
+ DebugTrace(0, Dbg, "QueryBasicInfo...\n", 0);
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ NwAcquireSharedFcb( Fcb->NonPagedFcb, TRUE );
+
+ //
+ // If we already know the file attributes, simply return them.
+ //
+
+ if ( FlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+
+ //
+ // Set the various fields in the record
+ //
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ Fcb->CreationDate,
+ Fcb->CreationTime
+ );
+
+ Buffer->LastAccessTime = NwDateTimeToNtTime(
+ Fcb->LastAccessDate,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastWriteTime = NwDateTimeToNtTime(
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime
+ );
+
+ DebugTrace(0, Dbg, "QueryBasic known %wZ\n", &Fcb->RelativeFileName);
+ DebugTrace(0, Dbg, "LastModifiedDate %x\n", Fcb->LastModifiedDate);
+ DebugTrace(0, Dbg, "LastModifiedTime %x\n", Fcb->LastModifiedTime);
+ DebugTrace(0, Dbg, "CreationDate %x\n", Fcb->CreationDate );
+ DebugTrace(0, Dbg, "CreationTime %x\n", Fcb->CreationTime );
+ DebugTrace(0, Dbg, "LastAccessDate %x\n", Fcb->LastAccessDate );
+
+ Buffer->FileAttributes = Fcb->NonPagedFcb->Attributes;
+
+ if ( Buffer->FileAttributes == 0 ) {
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return STATUS_SUCCESS;
+
+ } else if ( Fcb->RelativeFileName.Length == 0 ) {
+
+ //
+ // Allow 'cd \' to work.
+ //
+
+ Buffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ DEFAULT_DATE,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastAccessTime = Buffer->CreationTime;
+ Buffer->LastWriteTime = Buffer->CreationTime;
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return STATUS_SUCCESS;
+
+ } else {
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+Retry:
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ DebugTrace(0, Dbg, "QueryBasic short %wZ\n", &Fcb->RelativeFileName);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Fcb->Vcb->Specific.Disk.Handle,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b-==wwww",
+ 14,
+ &Attributes,
+ &CreationDate,
+ &LastAccessDate,
+ &LastModifiedDate,
+ &LastModifiedTime);
+ }
+
+ } else {
+
+ DebugTrace(0, Dbg, "QueryBasic long %wZ\n", &Fcb->RelativeFileName);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e_xx_xx_x",
+ 4,
+ &Attributes,
+ 12,
+ &CreationTime,
+ &CreationDate,
+ 4,
+ &LastModifiedTime,
+ &LastModifiedDate,
+ 4,
+ &LastAccessDate );
+
+ }
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Set the various fields in the record
+ //
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ CreationDate,
+ CreationTime
+ );
+
+ Buffer->LastAccessTime = NwDateTimeToNtTime(
+ LastAccessDate,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastWriteTime = NwDateTimeToNtTime(
+ LastModifiedDate,
+ LastModifiedTime
+ );
+
+ DebugTrace(0, Dbg, "CreationDate %x\n", CreationDate );
+ DebugTrace(0, Dbg, "CreationTime %x\n", CreationTime );
+ DebugTrace(0, Dbg, "LastAccessDate %x\n", LastAccessDate );
+ DebugTrace(0, Dbg, "LastModifiedDate %x\n", LastModifiedDate);
+ DebugTrace(0, Dbg, "LastModifiedTime %x\n", LastModifiedTime);
+
+ Buffer->FileAttributes = (UCHAR)Attributes;
+
+ if ( Buffer->FileAttributes == 0 ) {
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ } else if ((Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Fcb->Vcb );
+
+ goto Retry;
+ }
+
+ return( Status );
+ }
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastQueryBasicInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_BASIC_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is for the fast query call for standard file information.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the basic information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN - TRUE if the operation succeeded and FALSE if the caller
+ needs to take the long route.
+
+--*/
+
+{
+ NODE_TYPE_CODE NodeTypeCode;
+ PICB Icb;
+ PFCB Fcb;
+ PVOID FsContext;
+
+ //
+ // Find out who are.
+ //
+
+ if ((NodeTypeCode = NwDecodeFileObject( FileObject,
+ &FsContext,
+ &Icb )) != NW_NTC_ICB ) {
+
+ DebugTrace(-1, Dbg, "NwFastQueryStandardInfo -> FALSE\n", 0 );
+ return FALSE;
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+
+ NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
+
+ //
+ // If we don't have the info handy, we can't use the fast path.
+ //
+
+ if ( !FlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( FALSE );
+ }
+
+ //
+ // Set the various fields in the record
+ //
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ Fcb->CreationDate,
+ Fcb->CreationTime
+ );
+
+ Buffer->LastAccessTime = NwDateTimeToNtTime(
+ Fcb->LastAccessDate,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastWriteTime = NwDateTimeToNtTime(
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime
+ );
+
+ DebugTrace(0, Dbg, "QueryBasic known %wZ\n", &Fcb->RelativeFileName);
+ DebugTrace(0, Dbg, "LastModifiedDate %x\n", Fcb->LastModifiedDate);
+ DebugTrace(0, Dbg, "LastModifiedTime %x\n", Fcb->LastModifiedTime);
+ DebugTrace(0, Dbg, "CreationDate %x\n", Fcb->CreationDate );
+ DebugTrace(0, Dbg, "CreationTime %x\n", Fcb->CreationTime );
+ DebugTrace(0, Dbg, "LastAccessDate %x\n", Fcb->LastAccessDate );
+
+ Buffer->FileAttributes = Fcb->NonPagedFcb->Attributes;
+
+ if ( Buffer->FileAttributes == 0 ) {
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = sizeof( *Buffer );
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return TRUE;
+}
+#endif
+
+
+NTSTATUS
+NwQueryStandardInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_STANDARD_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine perforNw the query standard information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the being queried
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PFCB Fcb;
+ ULONG FileSize;
+ BOOLEAN FirstTime = TRUE;
+
+ PAGED_CODE();
+
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
+
+ //
+ // Fill in the answers we already know.
+ //
+
+ Buffer->NumberOfLinks = 1;
+
+ Buffer->DeletePending = (BOOLEAN)FlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+ Buffer->Directory = FALSE;
+ } else {
+ Buffer->Directory = TRUE;
+ }
+
+ if ( !Icb->HasRemoteHandle ) {
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ||
+ FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ //
+ // Allow 'cd \' to work.
+ //
+
+ Buffer->AllocationSize.QuadPart = 0;
+ Buffer->EndOfFile.QuadPart = 0;
+
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // No open handle for this file. Use a path based NCP
+ // to get the file size.
+ //
+Retry:
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_d",
+ 20,
+ &FileSize );
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_INFO_FILE_SIZE,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e",
+ 10,
+ &FileSize );
+ }
+
+ }
+
+ if ((Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Fcb->Vcb );
+
+ goto Retry;
+ }
+
+ Buffer->AllocationSize.QuadPart = FileSize;
+ Buffer->EndOfFile.QuadPart = FileSize;
+
+ }
+
+ } else {
+
+ //
+ // Start a Get file size NCP
+ //
+
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+ AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+ }
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof(Icb->Handle ) );
+
+ if ( NT_SUCCESS( Status ) ) {
+ //
+ // Get the data from the response.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileSize );
+
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Fill in Allocation size and EOF, based on the response.
+ //
+
+ Buffer->AllocationSize.QuadPart = FileSize;
+ Buffer->EndOfFile.QuadPart = Buffer->AllocationSize.QuadPart;
+
+ }
+ }
+
+ return( Status );
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastQueryStandardInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_STANDARD_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine is for the fast query call for standard file information.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the basic information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN - TRUE if the operation succeeded and FALSE if the caller
+ needs to take the long route.
+
+--*/
+{
+ NODE_TYPE_CODE NodeTypeCode;
+ PICB Icb;
+ PFCB Fcb;
+ PVOID FsContext;
+
+ //
+ // Find out who are.
+ //
+
+ if ((NodeTypeCode = NwDecodeFileObject( FileObject,
+ &FsContext,
+ &Icb )) != NW_NTC_ICB ) {
+
+ DebugTrace(-1, Dbg, "NwFastQueryStandardInfo -> FALSE\n", 0 );
+ return FALSE;
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // If we have the info handy, we can use the fast path.
+ //
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ||
+ FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ Buffer->AllocationSize.QuadPart = 0;
+ Buffer->EndOfFile.QuadPart = 0;
+
+ Buffer->NumberOfLinks = 1;
+ Buffer->DeletePending = (BOOLEAN)FlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ Buffer->Directory = TRUE;
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = sizeof( *Buffer );
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+
+ }
+}
+#endif
+
+
+NTSTATUS
+NwQueryInternalInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_INTERNAL_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine perforNw the query internal information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the being queried.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryInternalInfo...\n", 0);
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory( Buffer, sizeof(FILE_INTERNAL_INFORMATION) );
+
+ //
+ // Set the internal index number to be the address of the ICB.
+ //
+
+ Buffer->IndexNumber.LowPart = (ULONG)Icb->NpFcb;
+ Buffer->IndexNumber.HighPart = 0;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwQueryEaInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_EA_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query Ea information operation.
+
+Arguments:
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned
+
+Return Value:
+
+ VOID - The result of this query
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryEaInfo...\n", 0);
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory(Buffer, sizeof(FILE_EA_INFORMATION));
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NwQueryNameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_NAME_INFORMATION Buffer,
+ IN PULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query name information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the file to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned
+
+ Length - Supplies and receives the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ ULONG bytesToCopy;
+ ULONG fileNameSize;
+ PFCB Fcb = Icb->SuperType.Fcb;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryNameInfo...\n", 0);
+
+ //
+ // Win32 expects the root directory name to be '\' terminated,
+ // the netware server does not. So if this is a root directory,
+ // (i.e RelativeFileName length is 0) append a '\' to the path name.
+ //
+
+ //
+ // See if the buffer is large enough, and decide how many bytes to copy.
+ //
+
+ *Length -= FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0] );
+
+ fileNameSize = Fcb->FullFileName.Length;
+ if ( Fcb->RelativeFileName.Length == 0 ) {
+ fileNameSize += sizeof(L'\\');
+ }
+ Buffer->FileNameLength = fileNameSize;
+
+ if ( *Length >= fileNameSize ) {
+
+ status = STATUS_SUCCESS;
+
+ bytesToCopy = fileNameSize;
+
+ } else {
+
+ status = STATUS_BUFFER_OVERFLOW;
+
+ bytesToCopy = *Length;
+ }
+
+ //
+ // Copy over the file name and its length.
+ //
+
+ RtlMoveMemory(
+ Buffer->FileName,
+ Fcb->FullFileName.Buffer,
+ bytesToCopy);
+
+ //
+ // If this is a root directory, and there is space in the buffer
+ // append a '\' to make win32 happy.
+ //
+
+ if ( Fcb->RelativeFileName.Length == 0 && status == STATUS_SUCCESS ) {
+ Buffer->FileName[ fileNameSize/sizeof(WCHAR) - 1 ] = L'\\';
+ }
+
+ *Length -= bytesToCopy;
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query position information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the file being queried.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryPositionInfo...\n", 0);
+
+ //
+ // Return the current byte offset. This info is totally
+ // bogus for asynchronous files. Also note that we don't
+ // use the FilePosition member of the ICB for anything.
+ //
+
+ if ( Icb->FileObject ) {
+ Buffer->CurrentByteOffset.QuadPart = Icb->FileObject->CurrentByteOffset.QuadPart;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NwSetBasicInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB Icb,
+ IN PFILE_BASIC_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the basic information for a file.
+
+Arguments:
+
+ pIrpContext - Supplies Irp context information.
+
+ Icb - Supplies the ICB for the file being modified.
+
+ Buffer - Supplies the buffer containing the data being set.
+
+Return Value:
+
+ NTSTATUS - Returns our completion status.
+
+--*/
+
+{
+ PFCB Fcb;
+ NTSTATUS Status;
+ BOOLEAN SetTime = FALSE;
+ BOOLEAN SetAttributes = FALSE;
+ ULONG LfnFlag = 0;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "SetBasicInfo...\n", 0);
+
+ Fcb = Icb->SuperType.Fcb;
+
+ pIrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ //
+ // Append this IRP context and wait to get to the front.
+ // then grab from FCB
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+ NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ if (Buffer->CreationTime.QuadPart != 0) {
+
+ //
+ // Modify the creation time.
+ //
+
+ Status = NwNtTimeToNwDateTime(
+ Buffer->CreationTime,
+ &Fcb->CreationDate,
+ &Fcb->CreationTime );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ SetTime = TRUE;
+ LfnFlag |= LFN_FLAG_SET_INFO_CREATE_DATE | LFN_FLAG_SET_INFO_CREATE_TIME;
+ }
+
+ if (Buffer->LastAccessTime.QuadPart != 0) {
+
+ USHORT Dummy;
+
+ //
+ // Modify the last access time.
+ //
+
+ Status = NwNtTimeToNwDateTime(
+ Buffer->LastAccessTime,
+ &Fcb->LastAccessDate,
+ &Dummy );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ SetTime = TRUE;
+ LfnFlag |= LFN_FLAG_SET_INFO_LASTACCESS_DATE;
+
+ // Set the last access flag in the ICB so that we update
+ // last access time for real when we close this handle!
+
+ Icb->UserSetLastAccessTime = TRUE;
+ }
+
+ if (Buffer->LastWriteTime.QuadPart != 0) {
+
+ //
+ // Modify the last write time
+ //
+
+ Status = NwNtTimeToNwDateTime(
+ Buffer->LastWriteTime,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ LfnFlag |= LFN_FLAG_SET_INFO_MODIFY_DATE | LFN_FLAG_SET_INFO_MODIFY_TIME;
+ }
+
+
+ if (Buffer->FileAttributes != 0) {
+ LfnFlag |= LFN_FLAG_SET_INFO_ATTRIBUTES;
+ }
+
+ if ( LfnFlag == 0 ) {
+
+ //
+ // Nothing to set, simply return success.
+ //
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ //
+ // Call plain FlushCache - we don't want to acquire and
+ // release the NpFcb. We are already at the front and have the Fcb
+ // exclusive.
+ //
+
+ FlushCache( pIrpContext, Fcb->NonPagedFcb );
+ }
+
+ if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbWDW--WW==WW==_W_bDbC",
+ NCP_LFN_SET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ LfnFlag,
+ NtAttributesToNwAttributes( Buffer->FileAttributes ),
+ Fcb->CreationDate,
+ Fcb->CreationTime,
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime,
+ 8,
+ Fcb->LastAccessDate,
+ 8,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Fcb->RelativeFileName );
+
+ } else {
+
+ if ( LfnFlag & LFN_FLAG_SET_INFO_ATTRIBUTES ) {
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ NtAttributesToNwAttributes( Buffer->FileAttributes ),
+ Fcb->Vcb->Specific.Disk.Handle,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ &Fcb->RelativeFileName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ }
+
+#if 0
+ //
+ // We could conceivably use ScanDir/SetDir to update last access
+ // and create time. Not supported yet.
+ //
+
+ if ( LfnFlag & ( LFN_FLAG_SET_INFO_LASTACCESS_DATE | LFN_FLAG_SET_INFO_CREATE_DATE ) ) {
+
+ ULONG SearchIndex;
+ ULONG Directory;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SbbdU",
+ 0x16, 0x1E, // Scan dir entry
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0x06, // Search attributes
+ -1, // Search index
+ &Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Ndd",
+ &SearchIndex,
+ &Directory );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Sbbdddw=----_ww==ww==ww",
+ 0x16, 0x25, // Set dir entry
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0x06, // Search attributes
+ SearchIndex,
+ 0, // Change Bits?
+ Directory,
+ 12,
+ Fcb->CreationDate,
+ 0,
+ Fcb->LastAccessDate,
+ 0,
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime );
+ }
+ }
+#endif
+
+ if ( LfnFlag & LFN_FLAG_SET_INFO_MODIFY_DATE ) {
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F-rww-",
+ NCP_SET_FILE_TIME,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ Fcb->LastModifiedTime,
+ Fcb->LastModifiedDate );
+ }
+ }
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+
+ //
+ // And return to our caller
+ //
+
+ return Status;
+}
+
+
+NTSTATUS
+NwSetDispositionInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB Icb,
+ IN PFILE_DISPOSITION_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the disposition information for a file.
+
+Arguments:
+
+ pIrpContext - Supplies Irp context information.
+
+ Icb - Supplies the ICB for the file being modified.
+
+ Buffer - Supplies the buffer containing the data being set.
+
+Return Value:
+
+ NTSTATUS - Returns our completion status.
+
+--*/
+{
+ PFCB Fcb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "SetDispositionInfo...\n", 0);
+
+ Fcb = Icb->SuperType.Fcb;
+
+ if ( FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ //
+ // This is a print queue, just pretend this IRP succeeded.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // This is a real file or directory. Mark it delete pending.
+ //
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ pIrpContext->pNpScb = Fcb->Scb->pNpScb;
+ pIrpContext->Icb = Icb;
+
+ Icb->State = ICB_STATE_CLOSE_PENDING;
+
+ //
+ // Go ahead, delete the file.
+ //
+
+ Status = NwDeleteFile( pIrpContext );
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+NwDeleteFile(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine continues processing of the SetDispositionInfo request.
+ It must run in the redirector FSP.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PICB Icb;
+ PFCB Fcb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ Icb = pIrpContext->Icb;
+ Fcb = Icb->SuperType.Fcb;
+
+#if 0 // BUGBUG Was I on drugs? Below seems to be false, remove the check
+ ASSERT ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) );
+
+ //
+ // Do not allow delete of read-only file, the netware server will
+ // allow it.
+ //
+
+ if ( Icb->NpFcb->Attributes & NW_ATTRIBUTE_READ_ONLY ) {
+ return( STATUS_ACCESS_DENIED );
+ }
+#endif
+
+ ClearFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ //
+ // To a delete a file, first close the remote handle.
+ //
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Icb->HasRemoteHandle = FALSE;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+ }
+
+ //
+ // Note that this request cannot be reconnectable since, it can
+ // be called via NwCloseIcb(). See comment in that routine for
+ // more info.
+ //
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbbJ",
+ NCP_DELETE_FILE,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+
+ } else {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbW-DbC",
+ NCP_LFN_DELETE_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ NW_ATTRIBUTE_SYSTEM | NW_ATTRIBUTE_HIDDEN,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Fcb->RelativeFileName );
+ }
+
+ } else {
+
+ ASSERT( Fcb->NodeTypeCode == NW_NTC_DCB );
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_DELETE_DIRECTORY,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_DIRECTORIES,
+ &Fcb->RelativeFileName );
+ } else {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbW-DbC",
+ NCP_LFN_DELETE_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ SEARCH_ALL_DIRECTORIES,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Fcb->RelativeFileName );
+ }
+
+ }
+
+ if ( NT_SUCCESS( Status )) {
+
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N" );
+
+ } else {
+
+ //
+ // We can map all failures to STATUS_NO_SUCH_FILE
+ // except ACCESS_DENIED, which happens with a read
+ // only file.
+ //
+
+ if ( Status != STATUS_ACCESS_DENIED ) {
+ Status = STATUS_NO_SUCH_FILE;
+ }
+
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NwSetRenameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_RENAME_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine set rename information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS Status;
+ NTSTATUS Status2;
+ PFCB Fcb;
+ PFCB TargetFcb;
+ BOOLEAN HandleAllocated = FALSE;
+ BYTE Handle;
+ PICB TargetIcb = NULL;
+
+ UNICODE_STRING OldDrive;
+ UNICODE_STRING OldServer;
+ UNICODE_STRING OldVolume;
+ UNICODE_STRING OldPath;
+ UNICODE_STRING OldFileName;
+ UNICODE_STRING OldFullName;
+ WCHAR OldDriveLetter;
+ UNICODE_STRING OldFcbFullName;
+
+ UNICODE_STRING NewDrive;
+ UNICODE_STRING NewServer;
+ UNICODE_STRING NewVolume;
+ UNICODE_STRING NewPath;
+ UNICODE_STRING NewFileName;
+ UNICODE_STRING NewFullName;
+ WCHAR NewDriveLetter;
+ UNICODE_STRING NewFcbFullName;
+
+ USHORT i;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "SetRenameInfo...\n", 0);
+
+ //
+ // Can't try to set rename info on a print queue.
+ //
+
+ Fcb = Icb->SuperType.Fcb;
+
+ if ( FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace( 0, Dbg, " ->FullFileName = %wZ\n",
+ &Fcb->FullFileName);
+
+ if (irpSp->Parameters.SetFile.FileObject != NULL) {
+
+ TargetIcb = irpSp->Parameters.SetFile.FileObject->FsContext2;
+
+ DebugTrace( 0, Dbg, " ->FullFileName = %wZ\n",
+ &TargetIcb->SuperType.Fcb->FullFileName);
+
+ if ( TargetIcb->SuperType.Fcb->Scb != Icb->SuperType.Fcb->Scb ) {
+ return STATUS_NOT_SAME_DEVICE;
+ }
+
+ } else {
+
+ DebugTrace( 0, Dbg, " ->FullFileName in users buffer\n", 0);
+ DebugTrace(-1, Dbg, "SetRenameInfo %08lx\n", STATUS_NOT_IMPLEMENTED);
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ DebugTrace( 0, Dbg, " ->TargetFileName = %wZ\n",
+ &irpSp->Parameters.SetFile.FileObject->FileName);
+
+ TargetFcb = ((PNONPAGED_FCB)irpSp->Parameters.SetFile.FileObject->FsContext)->Fcb;
+
+
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
+
+ try {
+
+ //
+ // If either source or destination is a long name, use
+ // the long name path.
+ //
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
+ IsFatNameValid( &TargetFcb->RelativeFileName ) &&
+ !BooleanFlagOn( Fcb->Vcb->Flags, VCB_FLAG_LONG_NAME ) ) {
+
+ //
+ // Strip to UID portion of the FCB name.
+ //
+
+ for ( i = 0 ; i < Fcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
+ if ( Fcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
+ break;
+ }
+ }
+
+ ASSERT( Fcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
+
+ OldFcbFullName.Length = Fcb->FullFileName.Length - i*sizeof(WCHAR);
+ OldFcbFullName.Buffer = Fcb->FullFileName.Buffer + i;
+
+ Status = CrackPath (
+ &OldFcbFullName,
+ &OldDrive,
+ &OldDriveLetter,
+ &OldServer,
+ &OldVolume,
+ &OldPath,
+ &OldFileName,
+ &OldFullName );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Strip to UID portion of the FCB name.
+ //
+
+ TargetFcb = ((PNONPAGED_FCB)(irpSp->Parameters.SetFile.FileObject->FsContext))->Fcb;
+
+ for ( i = 0 ; i < TargetFcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
+ if ( TargetFcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
+ break;
+ }
+ }
+
+ ASSERT( TargetFcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
+
+ NewFcbFullName.Length = TargetFcb->FullFileName.Length - i*sizeof(WCHAR);
+ NewFcbFullName.Buffer = TargetFcb->FullFileName.Buffer + i;
+
+ Status = CrackPath (
+ &NewFcbFullName,
+ &NewDrive,
+ &NewDriveLetter,
+ &NewServer,
+ &NewVolume,
+ &NewPath,
+ &NewFileName,
+ &NewFullName );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Make sure that this is the same volume.
+ //
+
+ if ( RtlCompareUnicodeString( &NewVolume, &OldVolume, TRUE ) != 0 ) {
+ try_return( Status = STATUS_NOT_SAME_DEVICE );
+ }
+
+ if (Icb->SuperType.Fcb->IcbCount != 1) {
+ try_return( Status = STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // After a rename, the only operation allowed on the handle is an
+ // NtClose.
+ //
+
+ Icb->State = ICB_STATE_CLOSE_PENDING;
+
+ if ((irpSp->Parameters.SetFile.ReplaceIfExists ) &&
+ (TargetIcb->Exists)) {
+
+ // Delete the file
+
+ Status2 = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Fb-J",
+ NCP_DELETE_FILE,
+ TargetFcb->Vcb->Specific.Disk.Handle,
+ &TargetFcb->RelativeFileName );
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ //
+ // Need to create a handle to the directory containing the old
+ // file/directory name because directory rename does not contain a
+ // path and there might not be room for two paths in a file rename.
+ //
+ // The way we do this is to allocate a temporary handle on the server.
+ // This request is at the front of the Scb->Requests queue and so can
+ // use the temporary handle and delete it without affecting any other
+ // requests.
+ //
+
+ if ( OldPath.Length == 0 ) {
+
+ // In the root so use the VCB handle.
+
+ Handle = Fcb->Vcb->Specific.Disk.Handle;
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ", // NCP Allocate temporary directory handle
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE,
+ Fcb->Vcb->Specific.Disk.Handle,
+ '[',
+ &OldPath );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Handle );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ try_return(Status);
+ }
+
+ HandleAllocated = TRUE;
+ }
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ //
+ // We can only rename files in the same directory
+ //
+
+ if ( RtlCompareUnicodeString( &NewPath, &OldPath, TRUE ) != 0 ) {
+ try_return(Status = STATUS_NOT_SUPPORTED);
+
+ } else {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "SbJJ",
+ NCP_DIR_FUNCTION, NCP_RENAME_DIRECTORY,
+ Handle,
+ &OldFileName,
+ &NewFileName);
+ }
+
+ } else {
+
+ //
+ // We have to close the handle associated with the Icb that
+ // is doing the rename. Close that handle or the rename will
+ // fail for sure.
+ //
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Status2 = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ Icb->HasRemoteHandle = FALSE;
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ //
+ // Do the file rename Ncp.
+ //
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbJbJ",
+ NCP_RENAME_FILE,
+ Handle,
+ SEARCH_ALL_FILES,
+ &OldFileName,
+ Fcb->Vcb->Specific.Disk.Handle,
+ &NewFullName);
+ }
+
+ } else {
+
+ //
+ // We are going through the long name path. Ensure that the
+ // VCB supports long names.
+ //
+
+ if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace ==
+ LFN_NO_OS2_NAME_SPACE) {
+ try_return( Status = STATUS_OBJECT_PATH_SYNTAX_BAD );
+ }
+
+ if (Icb->SuperType.Fcb->IcbCount != 1) {
+ try_return( Status = STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // After a rename, the only operation allowed on the handle is an
+ // NtClose.
+ //
+
+ Icb->State = ICB_STATE_CLOSE_PENDING;
+
+ if ((irpSp->Parameters.SetFile.ReplaceIfExists ) &&
+ (TargetIcb->Exists)) {
+
+ // Delete the file
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbW-DbC",
+ NCP_LFN_DELETE_FILE,
+ TargetFcb->Vcb->Specific.Disk.LongNameSpace,
+ TargetFcb->Vcb->Specific.Disk.VolumeNumber,
+ SEARCH_ALL_FILES,
+ TargetFcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &TargetFcb->RelativeFileName );
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ //
+ // We can only rename files in the same directory
+ //
+
+ if ( Fcb->Vcb != TargetFcb->Vcb ) {
+ try_return(Status = STATUS_NOT_SUPPORTED);
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbbbDbbNN",
+ NCP_LFN_RENAME_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ 0, // Rename flag
+ SEARCH_ALL_DIRECTORIES,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &Fcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &TargetFcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ &Fcb->RelativeFileName,
+ &TargetFcb->RelativeFileName );
+ }
+
+ } else {
+
+ //
+ // We have to close the handle associated with the Icb that
+ // is doing the rename. Close that handle or the rename will
+ // fail for sure.
+ //
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Status2 = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ Icb->HasRemoteHandle = FALSE;
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ //
+ // Do the file rename Ncp.
+ //
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbbbDbbNN",
+ NCP_LFN_RENAME_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ 0, // Rename flag
+ SEARCH_ALL_FILES,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &Fcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &TargetFcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ &Fcb->RelativeFileName,
+ &TargetFcb->RelativeFileName );
+ }
+ }
+
+try_exit: NOTHING;
+ } finally {
+
+ if (HandleAllocated) {
+
+ Status2 = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb", // NCP Deallocate directory handle
+ NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
+ Handle);
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+
+ }
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ }
+
+ DebugTrace(-1, Dbg, "SetRenameInfo %08lx\n", Status );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NwSetPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets position information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT( Buffer->CurrentByteOffset.HighPart == 0 );
+
+ if ( Icb->FileObject ) {
+ Icb->FileObject->CurrentByteOffset.QuadPart = Buffer->CurrentByteOffset.QuadPart;
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwSetAllocationInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_ALLOCATION_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets allocation information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PFCB fcb = (PFCB)Icb->SuperType.Fcb;
+ PULONG pFileSize;
+
+ PAGED_CODE();
+
+ ASSERT( Buffer->AllocationSize.HighPart == 0);
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pFileSize = &Icb->NpFcb->Header.FileSize.LowPart;
+
+ IrpContext->pNpScb = fcb->Scb->pNpScb;
+
+ if (BooleanFlagOn( fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ return STATUS_SUCCESS;
+
+ }
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pFileSize = &Icb->FileSize;
+
+ IrpContext->pNpScb = ((PSCB)fcb)->pNpScb;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ DebugTrace( 0, Dbg, "NwSetAllocationInfo -> %08lx\n", STATUS_INVALID_PARAMETER );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( !Icb->HasRemoteHandle ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( Buffer->AllocationSize.LowPart == *pFileSize ) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+#ifndef QFE_BUILD
+ if ( Buffer->AllocationSize.LowPart < *pFileSize ) {
+
+ //
+ // Before we actually truncate, check to see if the purge
+ // is going to fail.
+ //
+
+ if (!MmCanFileBeTruncated( irpSp->FileObject->SectionObjectPointer,
+ &Buffer->AllocationSize )) {
+
+ return( STATUS_USER_MAPPED_FILE );
+ }
+ }
+#endif
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+ AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ }
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-rd=",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ Buffer->AllocationSize.LowPart );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pFileSize = Buffer->AllocationSize.LowPart;
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( Status );
+}
+
+NTSTATUS
+NwSetEndOfFileInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_END_OF_FILE_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets end of file information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PFCB fcb = (PFCB)Icb->SuperType.Fcb;
+ PULONG pFileSize;
+
+ PAGED_CODE();
+
+ ASSERT( Buffer->EndOfFile.HighPart == 0);
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pFileSize = &Icb->NpFcb->Header.FileSize.LowPart;
+
+ IrpContext->pNpScb = fcb->Scb->pNpScb;
+
+ if (BooleanFlagOn( fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ return STATUS_SUCCESS;
+
+ }
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pFileSize = &Icb->FileSize;
+
+ IrpContext->pNpScb = ((PSCB)fcb)->pNpScb;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ DebugTrace( 0, Dbg, "NwSetAllocationInfo -> %08lx\n", STATUS_INVALID_PARAMETER );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( !Icb->HasRemoteHandle ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( Buffer->EndOfFile.LowPart == *pFileSize ) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+#ifndef QFE_BUILD
+
+ if ( Buffer->EndOfFile.LowPart < *pFileSize ) {
+
+ //
+ // Before we actually truncate, check to see if the purge
+ // is going to fail.
+ //
+
+ if (!MmCanFileBeTruncated( irpSp->FileObject->SectionObjectPointer,
+ &Buffer->EndOfFile )) {
+
+ return( STATUS_USER_MAPPED_FILE );
+ }
+ }
+#endif
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+ AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ }
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-rd=",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ Buffer->EndOfFile.LowPart );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pFileSize = Buffer->EndOfFile.LowPart;
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( Status );
+}
+
+
+ULONG
+OccurenceCount (
+ IN PUNICODE_STRING String,
+ IN WCHAR SearchChar
+ )
+/*++
+
+Routine Description:
+
+ This routine counts the number of occurences of a search character
+ in a string
+
+Arguments:
+
+ String - The string to search
+
+ SearchChar - The character to search for.
+
+Return Value:
+
+ The occurence count.
+
+--*/
+{
+ PWCH currentChar;
+ PWCH endOfString;
+ ULONG count = 0;
+
+ PAGED_CODE();
+
+ currentChar = String->Buffer;
+ endOfString = &String->Buffer[ String->Length / sizeof(WCHAR) ];
+
+ while ( currentChar < endOfString ) {
+ if ( *currentChar == SearchChar ) {
+ count++;
+ }
+ currentChar++;
+ }
+
+ return( count );
+}
diff --git a/private/nw/rdr/filobsup.c b/private/nw/rdr/filobsup.c
new file mode 100644
index 000000000..64a5787ff
--- /dev/null
+++ b/private/nw/rdr/filobsup.c
@@ -0,0 +1,175 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ filobsup.c
+
+Abstract:
+
+ This module implements the Netware Redirector object support routines.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FILOBSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwSetFileObject )
+#pragma alloc_text( PAGE, NwDecodeFileObject )
+#endif
+
+
+VOID
+NwSetFileObject (
+ IN PFILE_OBJECT FileObject OPTIONAL,
+ IN PVOID FsContext,
+ IN PVOID FsContext2
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the file system pointers within the file object.
+
+Arguments:
+
+ FileObject - Supplies a pointer to the file object being modified, and
+ can optionally be null.
+
+ FsContext - Supplies a pointer to either an icb, fcb, vcb, or dcb
+ structure.
+
+ FsContext2 - Supplies a pointer to a icb, or is null.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwSetFileObject, FileObject = %08lx\n", (ULONG)FileObject );
+
+ //
+ // Set the fscontext fields of the file object.
+ //
+
+ FileObject->FsContext = FsContext;
+ FileObject->FsContext2 = FsContext2;
+
+ DebugTrace(-1, Dbg, "NwSetFileObject -> VOID\n", 0);
+
+ return;
+}
+
+
+NODE_TYPE_CODE
+NwDecodeFileObject (
+ IN PFILE_OBJECT FileObject,
+ OUT PVOID *FsContext,
+ OUT PVOID *FsContext2
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure takes a pointer to a file object, that has already been
+ opened by the mailslot file system and figures out what it really
+ is opened.
+
+Arguments:
+
+ FileObject - Supplies the file object pointer being interrogated
+
+ FsContext - Receives a pointer to the FsContext pointer
+ FsContext2 - Receives a pointer to the FsContext2 pointer
+
+Return Value:
+
+ NODE_TYPE_CODE - Returns the node type code for a Rcb, Scb, Dcb, Icb,
+ or zero.
+
+ Rcb - indicates that file object opens the netware redirector device.
+
+ Scb - indicates that file object is for a server.
+
+ Dcb - indicates that the file object is for a directory.
+
+ Icb - indicates that the file object is for a file.
+
+ Zero - indicates that the file object was for a netware file
+ but has been closed.
+
+--*/
+
+{
+ NODE_TYPE_CODE NodeTypeCode = NTC_UNDEFINED;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDecodeFileObject, FileObject = %08lx\n", (ULONG)FileObject);
+
+ //
+ // Read the fs FsContext fields of the file object.
+ //
+
+ *FsContext = FileObject->FsContext;
+ *FsContext2 = FileObject->FsContext2;
+
+ ASSERT ( *FsContext2 != NULL );
+ NodeTypeCode = NodeType( *FsContext2 );
+
+ DebugTrace(-1, Dbg, "NwDecodeFileObject -> %08lx\n", NodeTypeCode);
+ return NodeTypeCode;
+}
+
+BOOLEAN
+NwIsIrpTopLevel (
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine detects if an Irp is the Top level requestor, ie. if it is OK
+ to do a verify or pop-up now. If TRUE is returned, then no file system
+ resources are held above us.
+
+Arguments:
+
+ Irp - Supplies the Irp being processed
+
+ Status - Supplies the status to complete the Irp with
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if ( NwGetTopLevelIrp() == NULL ) {
+ NwSetTopLevelIrp( Irp );
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
diff --git a/private/nw/rdr/fragex.c b/private/nw/rdr/fragex.c
new file mode 100644
index 000000000..948fb09f8
--- /dev/null
+++ b/private/nw/rdr/fragex.c
@@ -0,0 +1,783 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ FragEx.c
+
+Abstract:
+
+ This module implements the fragment exchanger routine for
+ netware directory services access.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+Revision History:
+
+--*/
+
+#include <stdarg.h>
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+#pragma alloc_text( PAGE, FragExWithWait )
+#pragma alloc_text( PAGE, FormatBuf )
+#pragma alloc_text( PAGE, FormatBufS )
+
+NTSTATUS
+_cdecl
+FragExWithWait(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD NdsVerb,
+ IN PLOCKED_BUFFER pReplyBuffer,
+ IN BYTE *NdsRequestStr,
+ ...
+)
+/*
+
+Routine Description:
+
+ Exchanges an NDS request in fragments and collects the fragments
+ of the response. The buffer passed in much be locked down for
+ the transport.
+
+Routine Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+ NdsVerb - The verb for that indicates the request.
+
+ pReplyBuffer - The locked down reply buffer.
+
+ NdsReqestStr - The format string for the arguments to this NDS request.
+ Arguments - The arguments that satisfy the NDS format string.
+
+Return Value:
+
+ NTSTATUS - Status of the exchange, but not the result code in the packet.
+
+*/
+{
+
+ NTSTATUS Status;
+
+ BYTE *NdsRequestBuf;
+ DWORD NdsRequestLen;
+
+ BYTE *NdsRequestFrag, *NdsReplyFrag;
+ DWORD NdsRequestBytesLeft, NdsReplyBytesLeft, NdsReplyLen;
+
+ va_list Arguments;
+
+ PMDL pMdlSendData = NULL,
+ pTxMdlFrag = NULL,
+ pRxMdlFrag = NULL;
+
+ PMDL pOrigMdl;
+ DWORD OrigRxMdlSize;
+
+ DWORD MaxFragSize, SendFragSize;
+ DWORD ReplyFragSize, ReplyFragHandle;
+
+ DWORD NdsFraggerHandle = DUMMY_ITER_HANDLE;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "Entering FragExWithWait...\n", 0 );
+
+ //
+ // Allocate conversation buffer for the request.
+ //
+
+ NdsRequestBuf = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !NdsRequestBuf ) {
+
+ DebugTrace( 0, Dbg, "No memory for request buffer...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ //
+ // Build the request in our local buffer. Reserve the first
+ // five DWORDs for the NDS request header.
+ //
+
+ if ( NdsRequestStr != NULL ) {
+
+ va_start( Arguments, NdsRequestStr );
+
+ NdsRequestFrag = (BYTE *) NdsRequestBuf + sizeof( NDS_REQUEST_HEADER );
+
+ NdsRequestLen = FormatBuf( NdsRequestFrag,
+ NDS_BUFFER_SIZE - sizeof( NDS_REQUEST_HEADER ),
+ NdsRequestStr,
+ Arguments );
+
+ if ( !NdsRequestLen ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ va_end( Arguments );
+
+ } else {
+
+ NdsRequestLen = 0;
+ }
+
+ //
+ // Pack in the NDS preamble now that we know the length.
+ //
+ // The second DWORD in the preamble is the size of the NDS
+ // request which includes the three DWORDs immediately
+ // following the size in the preamble.
+ //
+
+ MaxFragSize = pIrpContext->pNpScb->BufferSize -
+ ( sizeof( NCP_REQUEST_WITH_SUB ) +
+ sizeof( NDS_REPLY_HEADER ) );
+
+ FormatBufS( NdsRequestBuf,
+ 5 * sizeof( DWORD ),
+ "DDDDD",
+ MaxFragSize, // max fragment size
+ NdsRequestLen + ( 3 * sizeof( DWORD ) ), // request size
+ 0, // fragment flags
+ NdsVerb, // nds verb
+ pReplyBuffer->dwRecvLen ); // reply buffer size
+
+ NdsRequestLen += sizeof( NDS_REQUEST_HEADER );
+
+ //
+ // Map the entire request to the SendData mdl and lock it down.
+ // we'll build partials into this data chunk as we proceed.
+ //
+
+ pMdlSendData = ALLOCATE_MDL( NdsRequestBuf,
+ NdsRequestLen,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !pMdlSendData ) {
+
+ DebugTrace( 0, Dbg, "Failed to allocate the request mdl...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ MmProbeAndLockPages( pMdlSendData, KernelMode, IoReadAccess );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Failed to lock request data in FragExWithWait!\n", 0 );
+ Status = GetExceptionCode();
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Allocate space for send and receive partial mdls.
+ //
+
+ pTxMdlFrag = ALLOCATE_MDL( NdsRequestBuf,
+ NdsRequestLen,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !pTxMdlFrag ) {
+
+ DebugTrace( 0, Dbg, "Failed to allocate a tx mdl for this fragment...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ pRxMdlFrag = ALLOCATE_MDL( pReplyBuffer->pRecvBufferVa,
+ pReplyBuffer->dwRecvLen,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !pRxMdlFrag ) {
+
+ DebugTrace( 0, Dbg, "Failed to allocate an rx mdl for this fragment...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Store the original RxMdl parameters and temporarily shorten it to hold
+ // only the response header.
+ //
+
+ pOrigMdl = pIrpContext->RxMdl->Next;
+ OrigRxMdlSize = MmGetMdlByteCount( pIrpContext->RxMdl );
+ pIrpContext->RxMdl->ByteCount = 16;
+
+ //
+ // The request is formatted, so set our internal pointers
+ // and start the exchange loop.
+ //
+
+ NdsReplyFrag = pReplyBuffer->pRecvBufferVa;
+ NdsReplyBytesLeft = pReplyBuffer->dwRecvLen;
+ NdsReplyLen = 0;
+
+ NdsRequestFrag = NdsRequestBuf;
+ NdsRequestBytesLeft = NdsRequestLen;
+
+ while ( TRUE ) {
+
+ //
+ // If there's more data to send in the request, set up the next MDL frag.
+ //
+
+ if ( NdsRequestBytesLeft ) {
+
+ if ( MaxFragSize < NdsRequestBytesLeft )
+ SendFragSize = MaxFragSize;
+ else
+ SendFragSize = NdsRequestBytesLeft;
+
+ IoBuildPartialMdl( pMdlSendData,
+ pTxMdlFrag,
+ NdsRequestFrag,
+ SendFragSize );
+
+ }
+
+ //
+ // Set up the response partial mdl with the buffer space that we have
+ // left. If we are here and there's no space left in the user's buffer,
+ // we're sort of hosed...
+ //
+
+ if ( !NdsReplyBytesLeft ) {
+
+ DebugTrace( 0, Dbg, "No room for fragment reply.\n", 0 );
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto ExitWithCleanup;
+
+ }
+
+ IoBuildPartialMdl( pReplyBuffer->pRecvMdl,
+ pRxMdlFrag,
+ NdsReplyFrag,
+ NdsReplyBytesLeft );
+
+ pIrpContext->RxMdl->Next = pRxMdlFrag;
+ pRxMdlFrag->Next = NULL;
+
+ //
+ // Do this transaction.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ if ( NdsRequestBytesLeft ) {
+
+ Status = ExchangeWithWait( pIrpContext,
+ SynchronousResponseCallback,
+ "NDf",
+ NDS_REQUEST, // NDS Function 104
+ NDS_ACTION, // NDS Subfunction 2
+ NdsFraggerHandle, // frag handle from the last response
+ pTxMdlFrag ); // NDS MDL Fragment
+
+ NdsRequestBytesLeft -= SendFragSize;
+ NdsRequestFrag = (LPBYTE) NdsRequestFrag + SendFragSize;
+ MmPrepareMdlForReuse( pTxMdlFrag );
+
+ //
+ // We may reuse this irp context, so we have to clear the
+ // TxMdl chain (Exchange doesn't do it for us).
+ //
+
+ pIrpContext->TxMdl->Next = NULL;
+
+ } else {
+
+ //
+ // There were no more request bytes to send, so we must have be allowed
+ // to continue to request another response fragment. NdsFraggerHandle
+ // contains the fragger handle from the last response.
+ //
+
+ Status = ExchangeWithWait( pIrpContext,
+ SynchronousResponseCallback,
+ "ND", // We only care about the frag handle
+ NDS_REQUEST, // NDS Function 104
+ NDS_ACTION, // NDS Subfunction 2
+ NdsFraggerHandle ); // the frag handle from last response
+ }
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Success? Get the frag size and frag handle and see.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Failed to exchange the fragment.\n", 0 );
+ goto ExitWithCleanup;
+
+ }
+
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp, // mapped into first rxmdl
+ 16, // only 16 bytes available
+ "NDD",
+ &ReplyFragSize,
+ &ReplyFragHandle );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // We got that fragment and it's already in our buffer. We have to adjust
+ // the index pointers, reset the MDLs, and continue on. Remember, we don't
+ // have to include space for the fragger handle since we've already got it.
+ //
+
+ ReplyFragSize -= sizeof( DWORD );
+
+ NdsReplyBytesLeft -= ReplyFragSize;
+ NdsReplyFrag = (LPBYTE) NdsReplyFrag + ReplyFragSize;
+ NdsReplyLen += ReplyFragSize;
+ MmPrepareMdlForReuse( pRxMdlFrag );
+
+ //
+ // Inspect the fraghandle.
+ //
+
+ if ( ReplyFragHandle == DUMMY_ITER_HANDLE ) {
+
+ // We are done!
+ //
+ // Invariant: There is a valid NDS response in the NdsReply
+ // and Status is NT_SUCCESS.
+
+ pReplyBuffer->dwBytesWritten = NdsReplyLen;
+ goto ExitWithCleanup;
+
+ } else {
+
+ // There's more coming! Remember the fragger handle and continue.
+
+ NdsFraggerHandle = ReplyFragHandle;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "Invalid state in FragExWithWait()\n", 0 );
+
+ExitWithCleanup:
+
+ //
+ // Unlock the request buffer and free its mdl.
+ //
+
+ if ( pMdlSendData ) {
+
+ MmUnlockPages( pMdlSendData );
+ FREE_MDL( pMdlSendData );
+ }
+
+ //
+ // Free the partial mdls.
+ //
+
+ if ( pRxMdlFrag )
+ FREE_MDL( pRxMdlFrag );
+
+ if ( pTxMdlFrag )
+ FREE_MDL( pTxMdlFrag );
+
+ //
+ // Free the request buffer.
+ //
+
+ FREE_POOL( NdsRequestBuf );
+
+ //
+ // Restore the original Irp->RxMdl parameters.
+ //
+
+ pIrpContext->RxMdl->Next = pOrigMdl;
+ pIrpContext->RxMdl->ByteCount = OrigRxMdlSize;
+
+ return Status;
+
+}
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+)
+/*
+
+Routine Description:
+
+ Formats a buffer according to supplied the format string.
+
+ FormatString - Supplies an ANSI string which describes how to
+ convert from the input arguments into NCP request fields, and
+ from the NCP response fields into the output arguments.
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'C' cstring followed skip word ( char*, word )
+ 'V' sized NDS value ( byte *, dword / byte **, dword *)
+ 'S' p unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 's' cstring copy as NDS_STRING (char* / char *, word)
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+
+Routine Arguments:
+
+ char *buf - destination buffer.
+ int buflen - length of the destination buffer.
+ char *format - format string.
+ args - args to the format string.
+
+Implementation Notes:
+
+ This comes almost verbatim from the Win95 source code. It duplicates
+ work in FormatRequest(). Eventually, FormatRequest() should be split
+ into two distinct routines: FormatBuffer() and MakeRequest().
+
+*/
+{
+ ULONG ix;
+
+ NTSTATUS status;
+ const char *z = format;
+
+ PAGED_CODE();
+
+ //
+ // Convert the input arguments into request packet.
+ //
+
+ ix = 0;
+
+ while ( *z )
+ {
+ switch ( *z )
+ {
+ case '=':
+ buf[ix++] = 0;
+ case '-':
+ buf[ix++] = 0;
+ break;
+
+ case '_':
+ {
+ WORD l = va_arg ( args, WORD );
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf case '_' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ while ( l-- )
+ buf[ix++] = 0;
+ break;
+ }
+
+ case 'b':
+ buf[ix++] = va_arg ( args, BYTE );
+ break;
+
+ case 'w':
+ {
+ WORD w = va_arg ( args, WORD );
+ buf[ix++] = (BYTE) (w >> 8);
+ buf[ix++] = (BYTE) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ DWORD d = va_arg ( args, DWORD );
+ buf[ix++] = (BYTE) (d >> 24);
+ buf[ix++] = (BYTE) (d >> 16);
+ buf[ix++] = (BYTE) (d >> 8);
+ buf[ix++] = (BYTE) (d >> 0);
+ break;
+ }
+
+ case 'W':
+ {
+ WORD w = va_arg(args, WORD);
+ (* (WORD *)&buf[ix]) = w;
+ ix += 2;
+ break;
+ }
+
+ case 'D':
+ {
+ DWORD d = va_arg (args, DWORD);
+ (* (DWORD *)&buf[ix]) = d;
+ ix += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( args, char* );
+ WORD l = strlen( c );
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf case 'c' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ RtlCopyMemory( &buf[ix], c, l+1 );
+ ix += l + 1;
+ break;
+ }
+
+ case 'C':
+ {
+ char* c = va_arg ( args, char* );
+ WORD l = va_arg ( args, WORD );
+ WORD len = strlen( c ) + 1;
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf 'C' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+
+ RtlCopyMemory( &buf[ix], c, len > l? l : len);
+ ix += l;
+ buf[ix-1] = 0;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( args, char* );
+ BYTE l = strlen( c );
+ if (ix + (ULONG)l +1 > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf case 'p' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ buf[ix++] = l;
+ RtlCopyMemory( &buf[ix], c, l );
+ ix += l;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( args, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if ( ix + Length > (ULONG)bufLen ) {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf case 'u' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+
+ buf[ix++] = (UCHAR)Length;
+ OemString.Buffer = &buf[ix];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ status = RtlUnicodeStringToOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ ix += (USHORT)Length;
+ break;
+ }
+
+ case 'S':
+ {
+ PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING);
+ ULONG Length, rLength;
+
+ Length = pUString->Length;
+ if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) {
+ DebugTrace( 0, Dbg, "FormatBuf: case 'S' request buffer too small.\n", 0 );
+ goto ErrorExit;
+ }
+
+ //
+ // The VLM client uses the rounded up length and it seems to
+ // make a difference! Also, don't forget that NDS strings have
+ // to be NULL terminated.
+ //
+
+ rLength = ROUNDUP4(Length + sizeof( WCHAR ));
+ *((DWORD *)&buf[ix]) = rLength;
+ ix += 4;
+ RtlCopyMemory(&buf[ix], pUString->Buffer, Length);
+ ix += Length;
+ rLength -= Length;
+ RtlFillMemory( &buf[ix], rLength, '\0' );
+ ix += rLength;
+ break;
+
+ }
+
+ case 's':
+ {
+ PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING);
+ ULONG Length, rLength;
+
+ Length = pUString->Length;
+ if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) {
+ DebugTrace( 0, Dbg, "FormatBuf: case 's' request buffer too small.\n", 0 );
+ goto ErrorExit;
+ }
+
+ //
+ // Don't use the padded size here, only the NDS null terminator.
+ //
+
+ rLength = Length + sizeof( WCHAR );
+ *((DWORD *)&buf[ix]) = rLength;
+ ix += 4;
+ RtlCopyMemory(&buf[ix], pUString->Buffer, Length);
+ ix += Length;
+ rLength -= Length;
+ RtlFillMemory( &buf[ix], rLength, '\0' );
+ ix += rLength;
+ break;
+
+
+ }
+
+ case 'V':
+ {
+ // too similar to 'S' - should be combined
+ BYTE* b = va_arg ( args, BYTE* );
+ DWORD l = va_arg ( args, DWORD );
+ if ( ix + l + sizeof(DWORD) > (ULONG)
+ bufLen )
+ {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf case 'V' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ *((DWORD *)&buf[ix]) = l;
+ ix += sizeof(DWORD);
+ RtlCopyMemory( &buf[ix], b, l );
+ l = ROUNDUP4(l);
+ ix += l;
+ break;
+ }
+
+ case 'r':
+ {
+ BYTE* b = va_arg ( args, BYTE* );
+ WORD l = va_arg ( args, WORD );
+ if ( ix + l > (ULONG)bufLen )
+ {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf case 'r' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ RtlCopyMemory( &buf[ix], b, l );
+ ix += l;
+ break;
+ }
+
+ default:
+
+#ifdef NWDBG
+ DbgPrint( "FormatBuf invalid request field, %x.\n", *z );
+#endif
+ ;
+
+ }
+
+ if ( ix > (ULONG)bufLen )
+ {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf: too much request data.\n" );
+#endif
+ goto ErrorExit;
+ }
+
+
+ z++;
+ }
+
+ return(ix);
+
+ErrorExit:
+ return 0;
+}
+
+
+
+int
+_cdecl
+FormatBufS(
+ char *buf,
+ int bufLen,
+ const char *format,
+ ...
+)
+/*++
+ args from the stack
+--*/
+{
+ va_list args;
+ int len;
+
+ PAGED_CODE();
+
+ va_start(args, format);
+ len = FormatBuf(buf, bufLen, format, args);
+ va_end( args );
+
+ return len;
+}
+
diff --git a/private/nw/rdr/fsctl.c b/private/nw/rdr/fsctl.c
new file mode 100644
index 000000000..159cd1385
--- /dev/null
+++ b/private/nw/rdr/fsctl.c
@@ -0,0 +1,5930 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ FsCtrl.c
+
+Abstract:
+
+ This module implements the File System Control routines for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 29-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include "ntddrdr.h"
+#include "ntddmup.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FSCTRL)
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonDeviceIoControl (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+StartRedirector(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+StopRedirector(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BindToTransport (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ChangePassword (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetInfo (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetDebug (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetMessage (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetStats (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetPrintJobId (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnectionDetails(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnectionPerformance(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+RegisterWithMup(
+ VOID
+ );
+
+VOID
+DeregisterWithMup(
+ VOID
+ );
+
+NTSTATUS
+QueryPath (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UserNcp(
+ ULONG Function,
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UserNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+FspCompleteLogin(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnection(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+EnumConnections(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+DeleteConnection(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteNetResourceEntry(
+ IN OUT PCHAR *FixedPortion,
+ IN OUT PWCHAR *EndOfVariableData,
+ IN PUNICODE_STRING ContainerName OPTIONAL,
+ IN PUNICODE_STRING LocalName OPTIONAL,
+ IN PUNICODE_STRING RemoteName,
+ IN ULONG ScopeFlag,
+ IN ULONG DisplayFlag,
+ IN ULONG UsageFlag,
+ IN ULONG ShareType,
+ OUT PULONG EntrySize
+ );
+
+BOOL
+CopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ );
+
+NTSTATUS
+GetRemoteHandle(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetUserName(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetChallenge(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteConnStatusEntry(
+ PSCB pConnectionScb,
+ PBYTE pbUserBuffer,
+ DWORD dwBufferLen,
+ DWORD *pdwBytesWritten,
+ DWORD *pdwBytesNeeded,
+ BOOLEAN fCallerScb
+ );
+
+NTSTATUS
+GetConnStatus(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ );
+
+NTSTATUS
+GetConnectionInfo(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetPreferredServer(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetShareBit(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ );
+
+//
+// Statics
+//
+
+HANDLE MupHandle;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdFileSystemControl )
+#pragma alloc_text( PAGE, NwCommonFileSystemControl )
+#pragma alloc_text( PAGE, NwFsdDeviceIoControl )
+#pragma alloc_text( PAGE, NwCommonDeviceIoControl )
+#pragma alloc_text( PAGE, BindToTransport )
+#pragma alloc_text( PAGE, ChangePassword )
+#pragma alloc_text( PAGE, SetInfo )
+#pragma alloc_text( PAGE, GetStats )
+#pragma alloc_text( PAGE, GetPrintJobId )
+#pragma alloc_text( PAGE, StartRedirector )
+#pragma alloc_text( PAGE, StopRedirector )
+#pragma alloc_text( PAGE, RegisterWithMup )
+#pragma alloc_text( PAGE, DeregisterWithMup )
+#pragma alloc_text( PAGE, QueryPath )
+#pragma alloc_text( PAGE, UserNcp )
+#pragma alloc_text( PAGE, GetConnection )
+#pragma alloc_text( PAGE, DeleteConnection )
+#pragma alloc_text( PAGE, WriteNetResourceEntry )
+#pragma alloc_text( PAGE, CopyStringToBuffer )
+#pragma alloc_text( PAGE, GetRemoteHandle )
+#pragma alloc_text( PAGE, GetUserName )
+#pragma alloc_text( PAGE, GetChallenge )
+#pragma alloc_text( PAGE, WriteConnStatusEntry )
+#pragma alloc_text( PAGE, GetConnStatus )
+#pragma alloc_text( PAGE, GetConnectionInfo )
+#pragma alloc_text( PAGE, GetPreferredServer )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, UserNcpCallback )
+#pragma alloc_text( PAGE1, GetConnectionDetails )
+#pragma alloc_text( PAGE1, GetMessage )
+#pragma alloc_text( PAGE1, EnumConnections )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+NTSTATUS
+NwFsdFileSystemControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of FileSystem control operations
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdFileSystemControl\n", 0);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+ Status = NwCommonFileSystemControl( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdFileSystemControl -> %08lx\n", Status);
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonFileSystemControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for doing FileSystem control operations called
+ by both the fsd and fsp threads
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ PIRP Irp;
+ ULONG Function;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ try {
+
+ //
+ // Get a pointer to the current Irp stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ Function = IrpSp->Parameters.FileSystemControl.FsControlCode;
+
+ DebugTrace(+1, Dbg, "NwCommonFileSystemControl\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
+ DebugTrace( 0, Dbg, "Function = %08lx\n", Function);
+ DebugTrace( 0, Dbg, "Function = %d\n", (Function >> 2) & 0x0fff);
+
+ //
+ // We know this is a file system control so we'll case on the
+ // minor function, and call a internal worker routine to complete
+ // the irp.
+ //
+
+ if (IrpSp->MinorFunction != IRP_MN_USER_FS_REQUEST ) {
+ DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ switch (Function) {
+
+ case FSCTL_NWR_START:
+ Status = StartRedirector( IrpContext );
+ break;
+
+ case FSCTL_NWR_STOP:
+ Status = StopRedirector( IrpContext );
+ break;
+
+ case FSCTL_NWR_LOGON:
+ Status = Logon( IrpContext );
+ break;
+
+ case FSCTL_NWR_LOGOFF:
+ Status = Logoff( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONNECTION:
+ Status = GetConnection( IrpContext );
+ break;
+
+ case FSCTL_NWR_ENUMERATE_CONNECTIONS:
+ Status = EnumConnections( IrpContext );
+ break;
+
+ case FSCTL_NWR_DELETE_CONNECTION:
+ Status = DeleteConnection( IrpContext );
+ break;
+
+ case FSCTL_NWR_BIND_TO_TRANSPORT:
+ Status = BindToTransport( IrpContext );
+ break;
+
+ case FSCTL_NWR_CHANGE_PASS:
+ Status = ChangePassword( IrpContext );
+ break;
+
+ case FSCTL_NWR_SET_INFO:
+ Status = SetInfo( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_DETAILS:
+ Status = GetConnectionDetails( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_MESSAGE:
+ Status = GetMessage( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_STATISTICS:
+ Status = GetStats( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_USERNAME:
+ Status = GetUserName( IrpContext );
+ break;
+
+ case FSCTL_NWR_CHALLENGE:
+ Status = GetChallenge( IrpContext );
+ break;
+
+ case FSCTL_GET_PRINT_ID:
+ Status = GetPrintJobId( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_STATUS:
+ Status = GetConnStatus( IrpContext, IrpSp->FileObject );
+ break;
+
+ case FSCTL_NWR_GET_CONN_INFO:
+ Status = GetConnectionInfo( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_PREFERRED_SERVER:
+ Status = GetPreferredServer( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_PERFORMANCE:
+ Status = GetConnectionPerformance( IrpContext );
+ break;
+
+ case FSCTL_NWR_SET_SHAREBIT:
+ Status = SetShareBit( IrpContext, IrpSp->FileObject );
+ break;
+
+ default:
+
+ if (( Function >= NWR_ANY_NCP(0)) &&
+ ( Function <= NWR_ANY_HANDLE_NCP(0x00ff))) {
+
+ Status = UserNcp( Function, IrpContext );
+ break;
+
+ }
+
+ if (( Function >= NWR_ANY_NDS(0)) &&
+ ( Function <= NWR_ANY_NDS(0x00ff))) {
+
+ Status = DispatchNds( Function, IrpContext );
+ break;
+ }
+
+ DebugTrace( 0, Dbg, "Invalid FS Control Code %08lx\n",
+ IrpSp->Parameters.FileSystemControl.FsControlCode);
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ }
+
+ } finally {
+
+ NwDereferenceUnlockableCodeSection ();
+
+ DebugTrace(-1, Dbg, "NwCommonFileSystemControl -> %08lx\n", Status);
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NwFsdDeviceIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of DeviceIoControl file operations
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdDeviceIoControl\n", 0);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+ Status = NwCommonDeviceIoControl( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest(IrpContext, Status);
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdDeviceIoControl -> %08lx\n", Status);
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonDeviceIoControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for doing FileSystem control operations called
+ by both the fsd and fsp threads
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ PIRP Irp;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ try {
+
+ //
+ // Get a pointer to the current Irp stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonDeviceIoControl\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
+ DebugTrace( 0, Dbg, "Function = %08lx\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ //
+ // We know this is a DeviceIoControl so we'll case on the
+ // minor function, and call a internal worker routine to complete
+ // the irp.
+ //
+
+ switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+
+ case IOCTL_REDIR_QUERY_PATH:
+ Status = QueryPath( IrpContext );
+ break;
+
+ case IOCTL_NWR_RAW_HANDLE:
+ Status = GetRemoteHandle( IrpContext );
+ break;
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid IO Control Code %08lx\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ } finally {
+
+ NwDereferenceUnlockableCodeSection ();
+ DebugTrace(-1, Dbg, "NwCommonDeviceIoControl -> %08lx\n", Status);
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+BindToTransport (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine records the name of the transport to be used and
+ initialises the PermanentScb.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ PAGED_CODE();
+
+#ifdef _PNP_POWER
+
+ //
+ // For PnP builds, register the bind handlers.
+ //
+
+ DebugTrace( 0 , Dbg, "Register TDI bind handlers.\n", 0 );
+
+ TdiInitialize();
+
+ return TdiRegisterNotificationHandler( HandleTdiBindMessage,
+ HandleTdiUnbindMessage,
+ &TdiBindingHandle );
+
+#endif
+
+ DebugTrace(+1, Dbg, "Bind to transport\n", 0);
+
+ try {
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ try_return( Status );
+ }
+
+ if (IpxHandle != NULL) {
+
+ //
+ // Can only bind to one transport at a time in this implementation
+ //
+
+ try_return(Status= STATUS_SHARING_VIOLATION);
+ }
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.Bind.TransportName)) +
+ InputBuffer->Parameters.Bind.TransportNameLength) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if ( IpxTransportName.Buffer != NULL ) {
+ FREE_POOL( IpxTransportName.Buffer );
+ }
+
+ Status = SetUnicodeString ( &IpxTransportName,
+ InputBuffer->Parameters.Bind.TransportNameLength,
+ InputBuffer->Parameters.Bind.TransportName);
+
+ DebugTrace(-1, Dbg, "\"%wZ\"\n", &IpxTransportName);
+
+ if ( !NT_SUCCESS(Status) ) {
+ try_return(Status);
+ }
+
+ Status = IpxOpen();
+ if ( !NT_SUCCESS(Status) ) {
+ try_return(Status);
+ }
+
+ //
+ // Verify that have a large enough stack size.
+ //
+
+ if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) {
+ IpxClose();
+ try_return( Status = STATUS_INVALID_PARAMETER );
+ }
+
+#ifndef QFE_BUILD
+
+ //
+ // Submit a line change request.
+ //
+
+ SubmitLineChangeRequest();
+#endif
+
+ //
+ // Open a handle to IPX.
+ //
+
+ NwPermanentNpScb.Server.Socket = 0;
+ Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = SetEventHandler (
+ IrpContext,
+ &NwPermanentNpScb.Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ &NwPermanentNpScb );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ IrpContext->pNpScb = &NwPermanentNpScb;
+
+ NwRcb.State = RCB_STATE_RUNNING;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Bind to transport\n", 0);
+ return Status;
+
+}
+
+#ifdef _PNP_POWER
+
+VOID
+HandleTdiBindMessage(
+ IN PUNICODE_STRING DeviceName
+)
+/*+++
+
+Description: This function is the bind handler for NetPnP
+ support. This function is registered with TDI and is called
+ whenever a transport starts up or stops. We watch for IPX
+ coming and going and do the appropriate thing.
+
+ See also: HandleTdiUnbindMessage()
+
+---*/
+{
+
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ PIRP pIrp = NULL;
+
+ PAGED_CODE();
+
+ //
+ // See if this is IPX requesting a bind. We only bind to NwLnkIpx.
+ //
+
+ if ( !RtlEqualUnicodeString( &TdiIpxDeviceName, DeviceName, TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Ignoring PnP Bind request for %wZ\n", DeviceName );
+ return;
+ }
+
+ //
+ // Make sure we aren't already bound.
+ //
+
+ if ( ( NwRcb.State != RCB_STATE_NEED_BIND ) ||
+ ( IpxHandle != NULL ) ) {
+
+ DebugTrace( 0, Dbg, "Discarding duplicate PnP bind request.\n", 0 );
+ return;
+ }
+
+ ASSERT( IpxTransportName.Buffer == NULL );
+ ASSERT( pIpxDeviceObject == NULL );
+
+ Status = DuplicateUnicodeStringWithString ( &IpxTransportName,
+ DeviceName,
+ PagedPool );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Failing IPX bind: Can't set device name.\n", 0 );
+ return;
+ }
+
+ //
+ // Open IPX.
+ //
+
+ Status = IpxOpen();
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that have a large enough stack size.
+ //
+
+ if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Submit a line change request.
+ //
+
+ SubmitLineChangeRequest();
+
+ //
+ // Allocate an irp and irp context. AllocateIrpContext may raise status.
+ //
+
+ pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE );
+
+ if ( pIrp == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ IrpContext = AllocateIrpContext( pIrp );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( IrpContext != NULL );
+
+ //
+ // Open a handle to IPX for the permanent scb.
+ //
+
+ NwPermanentNpScb.Server.Socket = 0;
+ Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = SetEventHandler (
+ IrpContext,
+ &NwPermanentNpScb.Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ &NwPermanentNpScb );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ IrpContext->pNpScb = &NwPermanentNpScb;
+
+ NwRcb.State = RCB_STATE_RUNNING;
+
+ DebugTrace( 0, Dbg, "Opened IPX for NwRdr.\n", 0 );
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we failed, clean up our globals.
+ //
+
+ if ( pIpxDeviceObject != NULL ) {
+ IpxClose();
+ pIpxDeviceObject = NULL;
+ }
+
+ IpxHandle = NULL;
+
+ if ( IpxTransportName.Buffer != NULL ) {
+ FREE_POOL( IpxTransportName.Buffer );
+ IpxTransportName.Buffer = NULL;
+ }
+
+ DebugTrace( 0, Dbg, "Failing IPX bind request.\n", 0 );
+
+ }
+
+ if ( pIrp != NULL ) {
+ FREE_IRP( pIrp );
+ }
+
+ if ( IrpContext != NULL ) {
+ FreeIrpContext( IrpContext );
+ }
+
+ return;
+
+}
+
+VOID
+HandleTdiUnbindMessage(
+ IN PUNICODE_STRING DeviceName
+)
+/*+++
+
+Description: This function is the unbind handler for NetPnP
+ support. This function is registered with TDI and is called
+ whenever a transport stops. We watch for IPX coming and going
+ and do the appropriate thing.
+
+ See also: HandleTdiBindMessage()
+
+---*/
+{
+
+ DebugTrace( 0, Dbg, "TDI unbind request ignored. Not Supported.\n", 0 );
+ return;
+
+}
+
+#endif
+
+
+NTSTATUS
+ChangePassword (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine records a change in the user's cached password.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER Uid;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "change password\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.ChangePass.UserName)) +
+ InputBuffer->Parameters.ChangePass.UserNameLength +
+ InputBuffer->Parameters.ChangePass.PasswordLength +
+ InputBuffer->Parameters.ChangePass.ServerNameLength ) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Get local pointer to the fsctl parameters
+ //
+
+ UserName.Buffer = InputBuffer->Parameters.ChangePass.UserName;
+ UserName.Length = (USHORT)InputBuffer->Parameters.ChangePass.UserNameLength;
+
+ Password.Buffer = UserName.Buffer +
+ (InputBuffer->Parameters.ChangePass.UserNameLength / 2);
+ Password.Length = (USHORT)InputBuffer->Parameters.ChangePass.PasswordLength;
+
+ ServerName.Buffer = Password.Buffer +
+ (InputBuffer->Parameters.ChangePass.PasswordLength / 2);
+ ServerName.Length = (USHORT)InputBuffer->Parameters.ChangePass.ServerNameLength;
+
+ //
+ // Update the default password for this user
+ //
+
+ Status = UpdateUsersPassword( &UserName, &Password, &Uid );
+
+ //
+ // Update the default password for this user
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ UpdateServerPassword( IrpContext, &ServerName, &UserName, &Password, &Uid );
+ }
+
+ Status = STATUS_SUCCESS;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Change Password\n", 0);
+ return Status;
+}
+
+
+NTSTATUS
+SetInfo (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine set netware redirector parameters.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Set info\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.SetInfo.PreferredServer)) +
+ InputBuffer->Parameters.SetInfo.PreferredServerLength +
+ InputBuffer->Parameters.SetInfo.ProviderNameLength ) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We don't do anything with a preferred server change, but if we
+ // get a request to change the preferred tree and context, we
+ // validate the context. The rest of the changes happen at the next
+ // login.
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.PreferredServerLength > 0 &&
+ InputBuffer->Parameters.SetInfo.PreferredServer[0] == '*' ) {
+
+ UNICODE_STRING Tree, NewContext;
+ USHORT i = 0;
+
+ //
+ // Dig out the tree name. Skip over the *.
+ //
+
+ Tree.Length = 0;
+ Tree.Buffer = InputBuffer->Parameters.SetInfo.PreferredServer + 1;
+
+ while ( i < InputBuffer->Parameters.SetInfo.PreferredServerLength ) {
+
+ if ( InputBuffer->Parameters.SetInfo.PreferredServer[i] == L'\\' ) {
+
+ i++;
+ Tree.Length -= sizeof( WCHAR );
+ Tree.MaximumLength = Tree.Length;
+ break;
+
+ } else {
+
+ Tree.Length += sizeof( WCHAR );
+ i++;
+
+ }
+ }
+
+ DebugTrace( 0, Dbg, "Tree: %wZ\n", &Tree );
+
+ NewContext.Length = (USHORT)InputBuffer->Parameters.SetInfo.PreferredServerLength -
+ ( Tree.Length + (2 * sizeof( WCHAR ) ) );
+ NewContext.Buffer = &InputBuffer->Parameters.SetInfo.PreferredServer[i];
+ NewContext.MaximumLength = NewContext.Length;
+
+ //
+ // Strip off any leading period.
+ //
+
+ if ( NewContext.Buffer[0] == L'.' ) {
+
+ NewContext.Buffer++;
+ NewContext.Length -= sizeof( WCHAR );
+ NewContext.MaximumLength -= sizeof( WCHAR );
+
+ }
+
+ DebugTrace( 0, Dbg, "Context: %wZ\n", &NewContext );
+
+ Status = NdsVerifyContext( IrpContext, &Tree, &NewContext );
+
+ if ( !NT_SUCCESS( Status )) {
+ try_return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ //
+ // Next set the provider name string.
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.ProviderNameLength != 0 ) {
+
+ PWCH TempBuffer;
+
+ TempBuffer = ALLOCATE_POOL_EX( PagedPool, InputBuffer->Parameters.SetInfo.ProviderNameLength );
+
+ if ( NwProviderName.Buffer != NULL ) {
+ FREE_POOL( NwProviderName.Buffer );
+ }
+
+ NwProviderName.Buffer = TempBuffer;
+ NwProviderName.Length = (USHORT)InputBuffer->Parameters.SetInfo.ProviderNameLength;
+
+ RtlCopyMemory(
+ NwProviderName.Buffer,
+ (PUCHAR)InputBuffer->Parameters.SetInfo.PreferredServer +
+ InputBuffer->Parameters.SetInfo.PreferredServerLength,
+ NwProviderName.Length );
+
+ }
+
+ //
+ // Set burst mode parameters
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize == 0 ) {
+ NwBurstModeEnabled = FALSE;
+ } else if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize != -1 ) {
+ NwBurstModeEnabled = TRUE;
+ NwMaxSendSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize;
+ NwMaxReceiveSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize;
+ }
+
+ //
+ // Set print options
+ //
+
+ NwPrintOptions = InputBuffer->Parameters.SetInfo.PrintOption;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Set info\n", 0);
+ return Status;
+}
+
+
+NTSTATUS
+GetMessage (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine queues an IRP to a list of IRP Contexts available for
+ reading server administrative messages.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PVOID OutputBuffer;
+
+ DebugTrace(+1, Dbg, "GetMessage\n", 0);
+
+ NwLockUserBuffer( Irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ IrpContext->pOriginalMdlAddress = Irp->MdlAddress;
+
+ IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer;
+ IrpContext->Specific.FileSystemControl.Length = OutputBufferLength;
+
+ ExInterlockedInsertTailList(
+ &NwGetMessageList,
+ &IrpContext->NextRequest,
+ &NwMessageSpinLock );
+
+ IoMarkIrpPending( Irp );
+
+ //
+ // Set the cancel routine.
+ //
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+
+ if ( Irp->Cancel ) {
+ NwCancelIrp( NULL, Irp );
+ } else {
+ IoSetCancelRoutine( Irp, NwCancelIrp );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+ }
+
+ DebugTrace(-1, Dbg, "Get Message -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetStats (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies Stats into the users buffer.
+
+ Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PVOID OutputBuffer;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetStats\n", 0);
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ if (NwRcb.State != RCB_STATE_RUNNING) {
+
+ Status = STATUS_REDIRECTOR_NOT_STARTED;
+
+ } else if (OutputBufferLength < sizeof(NW_REDIR_STATISTICS)) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if (OutputBufferLength != sizeof(NW_REDIR_STATISTICS)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ Stats.CurrentCommands = ContextCount;
+
+ RtlCopyMemory(OutputBuffer, &Stats, OutputBufferLength);
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = OutputBufferLength;
+
+ }
+
+ DebugTrace(-1, Dbg, "GetStats -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetPrintJobId (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the Job ID for this job.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PQUERY_PRINT_JOB_INFO OutputBuffer;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE NodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetJobId\n", 0);
+
+ NodeTypeCode = NwDecodeFileObject(
+ IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb );
+
+ if (NodeTypeCode != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( OutputBufferLength < sizeof( QUERY_PRINT_JOB_INFO ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ OutputBuffer->JobId = Icb->JobId;
+ //OutputBuffer->ServerName = BUGBUG
+ //OutputBuffer->QueueName = BUGBUG
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetJobId -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetConnectionDetails(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the details for a connection. This is normally used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_CONNECTION_DETAILS OutputBuffer;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetConnectionDetails\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB_SCB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status );
+
+ return Status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ pScb = (PSCB)Icb->SuperType.Scb;
+ nodeTypeCode = pScb->NodeTypeCode;
+
+ if (nodeTypeCode != NW_NTC_SCB) {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ pNpScb = pScb->pNpScb;
+
+ if ( OutputBufferLength < sizeof( NWR_GET_CONNECTION_DETAILS ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNextNpScb;
+ UCHAR OrderNumber;
+ OEM_STRING ServerName;
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ for ( ScbQueueEntry = ScbQueue.Flink, OrderNumber = 1;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = ScbQueueEntry->Flink, OrderNumber++ ) {
+
+ pNextNpScb = CONTAINING_RECORD(
+ ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+
+ //
+ // Check to make sure that this SCB is usable.
+ //
+
+ if ( pNextNpScb == pNpScb ) {
+ break;
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ OutputBuffer->OrderNumber = OrderNumber;
+
+ RtlZeroMemory( OutputBuffer->ServerName, sizeof(OutputBuffer->ServerName));
+ ServerName.Buffer = OutputBuffer->ServerName;
+ ServerName.Length = sizeof(OutputBuffer->ServerName);
+ ServerName.MaximumLength = sizeof(OutputBuffer->ServerName);
+ RtlUpcaseUnicodeStringToCountedOemString( &ServerName, &pNpScb->ServerName, FALSE);
+
+ RtlCopyMemory( OutputBuffer->ServerAddress,
+ &pNpScb->ServerAddress,
+ sizeof(OutputBuffer->ServerAddress) );
+
+ OutputBuffer->ServerAddress[12];
+ OutputBuffer->ConnectionNumberLo = pNpScb->ConnectionNo;
+ OutputBuffer->ConnectionNumberHi = pNpScb->ConnectionNoHigh;
+ // BUGBUG We need to ask the server during connect!
+ OutputBuffer->MajorVersion = 1;
+ OutputBuffer->MinorVersion = 11;
+ OutputBuffer->Preferred = pScb->PreferredServer;
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status );
+ return Status;
+}
+
+#if 0
+
+NTSTATUS
+GetOurAddress(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the value of OurAddress. This is normally used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_OUR_ADDRESS OutputBuffer;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ DebugTrace(+1, Dbg, "GetOurAddress\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB_SCB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status );
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ if ( OutputBufferLength < sizeof( NWR_GET_OUR_ADDRESS ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ RtlCopyMemory( OutputBuffer->Address,
+ &OurAddress,
+ sizeof(OurAddress );
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status );
+ return Status;
+}
+#endif
+
+
+NTSTATUS
+StartRedirector(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine starts the redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // We need to be in the FSP to Register the MUP.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return( Status );
+ }
+
+ NwRcb.State = RCB_STATE_STARTING;
+
+ FspProcess = PsGetCurrentProcess();
+
+#ifdef QFE_BUILD
+ StartTimer() ;
+#endif
+
+ //
+ // Now connect to the MUP.
+ //
+
+ RegisterWithMup();
+
+ KeQuerySystemTime( &Stats.StatisticsStartTime );
+
+ NwRcb.State = RCB_STATE_NEED_BIND;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+StopRedirector(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine shuts down the redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY LogonListEntry;
+ ULONG ActiveHandles;
+ ULONG RcbOpenCount;
+
+ PAGED_CODE();
+
+ //
+ // We need to be in the FSP to Deregister the MUP.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return( Status );
+ }
+
+#ifdef _PNP_POWER
+
+ //
+ // Unregister the bind handler with tdi.
+ //
+
+ if ( TdiBindingHandle != NULL ) {
+ TdiDeregisterNotificationHandler( TdiBindingHandle );
+ TdiBindingHandle = NULL;
+ }
+
+#endif
+
+ NwRcb.State = RCB_STATE_SHUTDOWN;
+
+ //
+ // Invalid all ICBs
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_SEND_ALWAYS );
+ ActiveHandles = NwInvalidateAllHandles(NULL, IrpContext);
+
+ //
+ // To expedite shutdown, set retry count down to 2.
+ //
+
+ DefaultRetryCount = 2;
+
+ //
+ // Close all VCBs
+ //
+
+ NwCloseAllVcbs( IrpContext );
+
+ //
+ // Logoff and disconnect from all servers.
+ //
+
+ NwLogoffAllServers( IrpContext, NULL );
+
+ while ( !IsListEmpty( &LogonList ) ) {
+
+ LogonListEntry = RemoveHeadList( &LogonList );
+
+ FreeLogon(CONTAINING_RECORD( LogonListEntry, LOGON, Next ));
+ }
+
+ InsertTailList( &LogonList, &Guest.Next ); // just in-case we don't unload.
+
+ StopTimer();
+
+ IpxClose();
+
+ //
+ // Remember the open count before calling DeristerWithMup since this
+ // will asynchronously cause handle count to get decremented.
+ //
+
+ RcbOpenCount = NwRcb.OpenCount;
+
+ DeregisterWithMup( );
+
+ DebugTrace(0, Dbg, "StopRedirector: Active handle count = %d\n", ActiveHandles );
+
+ //
+ // On shutdown, we need 0 remote handles and 2 open handles to
+ // the redir (one for the service, and one for the MUP) and the timer stopped.
+ //
+
+ if ( ActiveHandles == 0 && RcbOpenCount <= 2 ) {
+ return( STATUS_SUCCESS );
+ } else {
+ return( STATUS_REDIRECTOR_HAS_OPEN_HANDLES );
+ }
+}
+
+
+NTSTATUS
+RegisterWithMup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine register this redirector as a UNC provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING RdrName;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &RdrName, DD_NWFS_DEVICE_NAME_U );
+ Status = FsRtlRegisterUncProvider(
+ &MupHandle,
+ &RdrName,
+ FALSE // Do not support mailslots
+ );
+
+ return( Status );
+}
+
+
+
+VOID
+DeregisterWithMup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine deregisters this redirector as a UNC provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ FsRtlDeregisterUncProvider( MupHandle );
+}
+
+
+NTSTATUS
+QueryPath(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies whether a path is a netware path.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PQUERY_PATH_REQUEST qpRequest;
+ PQUERY_PATH_RESPONSE qpResponse;
+ UNICODE_STRING FilePathName;
+ ULONG OutputBufferLength;
+ ULONG InputBufferLength;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+
+ UNICODE_STRING DriveName;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING VolumeName;
+ UNICODE_STRING PathName;
+ UNICODE_STRING FileName;
+ UNICODE_STRING UnicodeUid;
+ WCHAR DriveLetter;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ ASSERT (( IOCTL_REDIR_QUERY_PATH & 3) == METHOD_NEITHER);
+
+ RtlInitUnicodeString( &UnicodeUid, NULL );
+
+ try {
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ //
+ // The input buffer is either in Irp->AssociatedIrp.SystemBuffer, or
+ // in the Type3InputBuffer for type 3 IRP's.
+ //
+
+ qpRequest = (PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+ qpResponse = (PQUERY_PATH_RESPONSE)qpRequest;
+
+ ASSERT( qpRequest != NULL );
+
+ FilePathName.Buffer = qpRequest->FilePathName;
+ FilePathName.Length = (USHORT)qpRequest->PathNameLength;
+
+ status = CrackPath( &FilePathName, &DriveName, &DriveLetter, &ServerName, &VolumeName, &PathName, &FileName, NULL );
+
+ if (( !NT_SUCCESS( status ) ) ||
+ ( ServerName.Length == 0 )) {
+
+ try_return( status = STATUS_BAD_NETWORK_PATH );
+ }
+
+ qpResponse->LengthAccepted = VolumeName.Length;
+
+ //
+ // As far as the redirector is concerned, QueryPath is a form
+ // of create. Set up the IrpContext appropriately.
+ //
+
+ IrpContext->Specific.Create.VolumeName = VolumeName;
+ IrpContext->Specific.Create.PathName = PathName;
+ IrpContext->Specific.Create.DriveLetter = DriveLetter;
+ IrpContext->Specific.Create.FullPathName = FilePathName;
+
+ RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL );
+
+ //
+ // The irp context specific data is now zeroed out by AllocateIrpContext,
+ // so we don't have to worry about re-setting the specific data here.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+
+ IrpContext->Specific.Create.UserUid = GetUid( &SubjectContext );
+
+ SeReleaseSubjectContext(&SubjectContext);
+
+ try {
+
+ //
+ // The slightly more complicated approach. This function
+ // handles the resolution of the server/volume duple. It
+ // may use the bindery, cached nds information, or fresh
+ // nds information.
+ //
+
+ status = HandleVolumeAttach( IrpContext,
+ &ServerName,
+ &VolumeName );
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+ status = STATUS_BAD_NETWORK_PATH;
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ RtlFreeUnicodeString(&UnicodeUid);
+ }
+
+ return( status );
+}
+
+NTSTATUS
+UserNcp(
+ ULONG IoctlCode,
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges an NCP with the server.
+
+ BUGBUG - We need to filter or security check what the user is
+ doing.
+
+Arguments:
+
+ IoctlCode - Supplies the code to be used for the NCP.
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+ PCHAR InputBuffer;
+ ULONG InputBufferLength;
+
+ PICB icb;
+ PSCB pScb;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ UCHAR Function = ANY_NCP_OPCODE( IoctlCode );
+ UCHAR Subfunction = 0;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ DebugTrace(+1, DEBUG_TRACE_USERNCP, "UserNcp...\n", 0);
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "irp = %08lx\n", (ULONG)irp);
+
+ //
+ // This F2 and ANY NCP must be addressed either to \Device\NwRdr or
+ // \Device\NwRdr\<servername> any additional name is not allowed.
+ // If the handle used for the Irp specifies \Device\NwRdr then the
+ // redirector gets to choose among the connected servers.
+ //
+ // For HANDLE NCP the file must be an FCB.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ if ((nodeTypeCode == NW_NTC_ICB_SCB) &&
+ (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode))) {
+
+ // All ok
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ pScb = (PSCB)icb->SuperType.Scb;
+ nodeTypeCode = pScb->NodeTypeCode;
+
+ IrpContext->pScb = pScb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ } else if (nodeTypeCode == NW_NTC_ICB) {
+
+ if ((IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) &&
+ (InputBufferLength < 7)) {
+
+ // Buffer needs enough space for the handle!
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Not enough space for handle %x\n", InputBufferLength);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ // Let through FCB's and DCB's
+ //
+
+ NwVerifyIcb( icb );
+
+ pScb = (PSCB)icb->SuperType.Fcb->Scb;
+ nodeTypeCode = icb->SuperType.Fcb->NodeTypeCode;
+
+ IrpContext->pScb = pScb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ //
+ // Set the icb pointer in case the cache gets
+ // flushed because the write routines look at it.
+ //
+
+ IrpContext->Icb = icb;
+ AcquireFcbAndFlushCache( IrpContext, icb->NpFcb );
+
+ } else {
+
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", irpSp->FileObject);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+ }
+
+ if (icb->Pid == INVALID_PID) {
+ status = NwMapPid( (ULONG)PsGetCurrentThread(), &icb->Pid );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp Pid = %02lx\n", icb->Pid );
+ NwSetEndOfJobRequired(icb->Pid);
+
+ }
+
+ //
+ // We now know where to send the NCP. Lock down the users buffers and
+ // build the Mdls required to transfer the data.
+ //
+
+ InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ if ( OutputBufferLength ) {
+ NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
+ } else {
+ OutputBuffer = NULL;
+ }
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ if (InputBufferLength != 0) {
+ if (IS_IT_NWR_ANY_NCP(IoctlCode)) {
+ Subfunction = InputBuffer[0];
+ } else if (InputBufferLength >= 3) {
+ Subfunction = InputBuffer[2];
+ }
+ }
+
+
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "UserNcp function = %x\n", Function );
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, " & Subfunction = %x\n", Subfunction );
+ dump( DEBUG_TRACE_USERNCP, InputBuffer, InputBufferLength );
+ //dump( DEBUG_TRACE_USERNCP, OutputBuffer, OutputBufferLength );
+
+ if ((Function == NCP_ADMIN_FUNCTION ) &&
+ (InputBufferLength >= 4 )) {
+
+ if ( ( (Subfunction == NCP_SUBFUNC_79) ||
+ (Subfunction == NCP_CREATE_QUEUE_JOB ) ) &&
+ icb->HasRemoteHandle) {
+
+ //
+ // Trying to create a job on a queue that already has a job
+ // on it. Cancel the old job.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB, // Close File And Cancel Queue Job
+ icb->SuperType.Fcb->Vcb->Specific.Print.QueueId,
+ icb->JobId );
+
+ if (!NT_SUCCESS(status)) {
+
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "DeleteOldJob got status -> %08lx\n", status );
+ // Don't worry if the delete fails, proceed with the create
+ }
+
+ icb->IsPrintJob = FALSE; // App will have to queue or cancel job, not rdr
+
+ } else if ((Subfunction == NCP_PLAIN_TEXT_LOGIN ) ||
+ (Subfunction == NCP_ENCRYPTED_LOGIN )) {
+
+ UNICODE_STRING UserName;
+ OEM_STRING OemUserName;
+ PUCHAR InputBuffer;
+
+ //
+ // Trying to do a login.
+ //
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Assume success, store the user name in the SCB.
+ //
+
+ try {
+
+ InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ OemUserName.Length = InputBuffer[ 13 ];
+ OemUserName.Buffer = &InputBuffer[14];
+
+ UserName.MaximumLength = OemUserName.Length * sizeof(WCHAR);
+ if ( OemUserName.Length == 0 || OemUserName.Length > MAX_USER_NAME_LENGTH ) {
+ try_return( status = STATUS_NO_SUCH_USER );
+ }
+
+ UserName.Buffer = ALLOCATE_POOL_EX( NonPagedPool, UserName.MaximumLength );
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemUserName.Length != 0 ) {
+ status = RtlOemStringToCountedUnicodeString( &UserName, &OemUserName, FALSE );
+ } else {
+ UserName.Length = 0;
+ }
+try_exit: NOTHING;
+ } finally {
+ NOTHING;
+ }
+
+ if ( NT_SUCCESS( status )) {
+
+ if ( pScb->OpenFileCount != 0 &&
+ pScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ if (!RtlEqualUnicodeString( &pScb->UserName, &UserName, TRUE )) {
+
+ //
+ // But were already logged in to this server and at
+ // least one other handle is using the connection and
+ // the user is trying to change the username.
+ //
+
+ FREE_POOL( UserName.Buffer );
+ return STATUS_NETWORK_CREDENTIAL_CONFLICT;
+
+ } else {
+
+ PUCHAR VerifyBuffer = ALLOCATE_POOL( PagedPool, InputBufferLength );
+
+ //
+ // Same username. Validate password is correct.
+
+ if (VerifyBuffer == NULL) {
+ FREE_POOL( UserName.Buffer );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( VerifyBuffer, InputBuffer, InputBufferLength );
+
+ if (IS_IT_NWR_ANY_NCP(IoctlCode)) {
+ VerifyBuffer[0] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ?
+ NCP_PLAIN_TEXT_VERIFY_PASSWORD:
+ NCP_ENCRYPTED_VERIFY_PASSWORD;
+
+ } else {
+ VerifyBuffer[2] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ?
+ NCP_PLAIN_TEXT_VERIFY_PASSWORD:
+ NCP_ENCRYPTED_VERIFY_PASSWORD;
+ }
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr",
+ Function, VerifyBuffer[0],
+ &VerifyBuffer[1], InputBufferLength - 1 );
+
+ FREE_POOL( UserName.Buffer );
+ FREE_POOL( VerifyBuffer );
+ return status;
+
+ }
+ }
+
+ if (pScb->UserName.Buffer) {
+ FREE_POOL( pScb->UserName.Buffer ); // May include space for password too.
+ }
+
+ IrpContext->pNpScb->pScb->UserName = UserName;
+ IrpContext->pNpScb->pScb->Password.Buffer = UserName.Buffer;
+ IrpContext->pNpScb->pScb->Password.Length = 0;
+
+ } else {
+ return( status );
+ }
+ }
+ } else if (Function == NCP_LOGOUT ) {
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( pScb->OpenFileCount == 0 &&
+ pScb->pNpScb->State == SCB_STATE_IN_USE &&
+ !pScb->PreferredServer ) {
+
+ NwLogoffAndDisconnect( IrpContext, pScb->pNpScb);
+ return STATUS_SUCCESS;
+
+ } else {
+
+ return(STATUS_CONNECTION_IN_USE);
+
+ }
+ }
+
+ IrpContext->Icb = icb;
+
+ //
+ // Remember where the response goes.
+ //
+
+ IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer;
+ IrpContext->Specific.FileSystemControl.Length = OutputBufferLength;
+
+ IrpContext->Specific.FileSystemControl.Function = Function;
+ IrpContext->Specific.FileSystemControl.Subfunction = Subfunction;
+
+ //
+ // Decide how to send the buffer. If it is small enough, send it
+ // by copying the user buffer to our send buffer. If it is bigger
+ // we will need to build an MDL for the user's buffer, and used a
+ // chained send.
+ //
+
+ if ( InputBufferLength == 0 ) {
+
+ // Simple request such as systime.exe
+
+ IrpContext->Specific.FileSystemControl.InputMdl = NULL;
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ "F", Function);
+
+ } else if ( InputBufferLength < MAX_SEND_DATA - sizeof( NCP_REQUEST ) - 2 ) {
+
+ //
+ // Send the request by copying it to our send buffer.
+ //
+
+ IrpContext->Specific.FileSystemControl.InputMdl = NULL;
+
+ if (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) {
+
+ //
+ // E0, E1, E2 and E3 get mapped to 14,15,16 and 17. These need
+ // a length word before the buffer.
+ //
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr",
+ Function, InputBuffer[0],
+ &InputBuffer[1], InputBufferLength - 1 );
+ } else {
+
+ //
+ // Replace the 6 bytes of InputBuffer starting at offset 1
+ // with the 6 byte NetWare address for this icb. This request
+ // is used in some of the 16 bit NCP's used for file locking.
+ // These requests are always fairly small.
+ //
+
+ if (!icb->HasRemoteHandle) {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ "Fbrr",
+ Function,
+ InputBuffer[0],
+ &icb->Handle, sizeof(icb->Handle),
+ &InputBuffer[7], InputBufferLength - 7 );
+ }
+
+ } else {
+
+ PMDL pMdl = NULL;
+
+ if (IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // We need to chain send the request. Allocate an MDL.
+ //
+
+ try {
+ pMdl = ALLOCATE_MDL(
+ &InputBuffer[1],
+ InputBufferLength - 1,
+ TRUE, // Secondary MDL
+ TRUE, // Charge quota
+ NULL );
+
+ if ( pMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmProbeAndLockPages( pMdl, irp->RequestorMode, IoReadAccess );
+
+ //
+ // Remember the MDL so we can free it.
+ //
+
+ IrpContext->Specific.FileSystemControl.InputMdl = pMdl;
+
+ //
+ // Send the request.
+ //
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sf":"Fbf",
+ Function, InputBuffer[0],
+ pMdl );
+
+
+ } finally {
+
+ if ((status != STATUS_PENDING ) &&
+ ( pMdl != NULL)) {
+
+ FREE_MDL( pMdl );
+
+ }
+ }
+ }
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+}
+
+
+
+NTSTATUS
+UserNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PIRP Irp;
+ ULONG Length;
+ PICB Icb = IrpContext->Icb;
+ PEPresponse *pResponseParameters;
+
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "UserNcpCallback...\n", 0);
+
+ if ( IrpContext->Specific.FileSystemControl.InputMdl != NULL ) {
+ MmUnlockPages( IrpContext->Specific.FileSystemControl.InputMdl );
+ FREE_MDL( IrpContext->Specific.FileSystemControl.InputMdl );
+ }
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ dump( DEBUG_TRACE_USERNCP, Response, BytesAvailable );
+
+ Buffer = IrpContext->Specific.FileSystemControl.Buffer;
+ BufferLength = IrpContext->Specific.FileSystemControl.Length;
+
+ //
+ // Get the data from the response.
+ //
+
+ Length = MIN( BufferLength, BytesAvailable - 8 );
+
+ if (IrpContext->Specific.FileSystemControl.Function == NCP_ADMIN_FUNCTION ) {
+
+ if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_79) {
+
+ //
+ // Create Queue Job and File Ncp. If the operation was a success
+ // then we need to save the handle. This will allow Write Irps
+ // on this Icb to be sent to the server.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N_r",
+ 0x3E,
+ Icb->Handle+2,4);
+
+ // Pad the handle to its full 6 bytes.
+ Icb->Handle[0] = 0;
+ Icb->Handle[1] = 0;
+
+ if (NT_SUCCESS(Status)) {
+ Icb->HasRemoteHandle = TRUE;
+ }
+
+ //
+ // Reset the file offset.
+ //
+
+ Icb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ } else if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CREATE_QUEUE_JOB ) {
+
+ //
+ // Create Queue Job and File Ncp. If the operation was a success
+ // then we need to save the handle. This will allow Write Irps
+ // on this Icb to be sent to the server.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N_r",
+ 0x2A,
+ Icb->Handle,6);
+
+ if (NT_SUCCESS(Status)) {
+ Icb->HasRemoteHandle = TRUE;
+ }
+
+ //
+ // Reset the file offset.
+ //
+
+ Icb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_7F) ||
+ (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CLOSE_FILE_AND_START_JOB )) {
+
+ // End Job request
+
+ Icb->HasRemoteHandle = FALSE;
+
+ } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_PLAIN_TEXT_LOGIN ) ||
+ (IrpContext->Specific.FileSystemControl.Subfunction == NCP_ENCRYPTED_LOGIN )) {
+
+ //
+ // Trying to do a login from a 16 bit application.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N" );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+
+ //
+ // Set the reconnect attempt flag so that we don't try to
+ // run this irp context through the reconnect logic. Doing
+ // this could deadlock the worker thread that's handling this
+ // fsp side request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+ IrpContext->PostProcessRoutine = FspCompleteLogin;
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return Status;
+
+ } else {
+ if (IrpContext->pNpScb->pScb->UserName.Buffer) {
+ FREE_POOL( IrpContext->pNpScb->pScb->UserName.Buffer );
+ }
+ RtlInitUnicodeString( &IrpContext->pNpScb->pScb->UserName, NULL);
+ RtlInitUnicodeString( &IrpContext->pNpScb->pScb->Password, NULL);
+ }
+ }
+ }
+
+ pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1);
+
+ ParseResponse( IrpContext, Response, BytesAvailable, "Nr", Buffer, Length );
+
+ Status = ( ( pResponseParameters->status &
+ ( NCP_STATUS_BAD_CONNECTION |
+ NCP_STATUS_NO_CONNECTIONS |
+ NCP_STATUS_SERVER_DOWN ) ) << 8 ) |
+ pResponseParameters->error;
+
+ if ( Status ) {
+ //
+ // Use the special error code that will cause conversion
+ // of the status back to a Dos error code to leave status and
+ // error unchanged. This is necessary because many of the
+ // NetWare error codes have different meanings depending on the
+ // operation being performed.
+ //
+
+ Status |= 0xc0010000;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ Irp->IoStatus.Information = Length;
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+FspCompleteLogin(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine reopens any Vcb directory handles.
+ It also sets the Scb as in use. This could have been done
+ in the callback routine too.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+
+ IrpContext->pNpScb->State = SCB_STATE_IN_USE;
+
+ ReconnectScb( IrpContext, IrpContext->pScb );
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+GetConnection(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the path of a connection.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_SERVER_RESOURCE OutputBuffer;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PVCB Vcb;
+ PWCH DriveName;
+ ULONG DriveNameLength;
+ UNICODE_STRING Path;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "GetConnection...\n", 0);
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Find the VCB
+ //
+
+ DriveName = InputBuffer->Parameters.GetConn.DeviceName;
+ DriveNameLength = InputBuffer->Parameters.GetConn.DeviceNameLength;
+ Vcb = NULL;
+
+ if ( DriveName[0] >= L'A' && DriveName[0] <= L'Z' &&
+ DriveName[1] == L':' &&
+ DriveNameLength == sizeof( L"X:" ) - sizeof( L'\0' ) ) {
+
+ Vcb = DriveMapTable[DriveName[0] - 'A'];
+
+ } else if ( _wcsnicmp( DriveName, L"LPT", 3 ) == 0 &&
+ DriveName[3] >= '1' && DriveName[3] <= '9' &&
+ DriveNameLength == sizeof( L"LPTX" ) - sizeof( L'\0' ) ) {
+
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveName[3] - '1'];
+ }
+
+ if ( Vcb == NULL) {
+ try_return( Status = STATUS_NO_SUCH_FILE );
+ }
+
+ OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer;
+ OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ //
+ // Calculate the VCB path to return.
+ //
+ // BUGBUG. We shouldn't have to recalc all the time. Add a
+ // new string the the VCB to remember this info. Init it
+ // in NwCreateVcb.
+ //
+
+ if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
+ Path.Buffer = Vcb->Name.Buffer + 3;
+ Path.Length = Vcb->Name.Length - 6;
+ } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
+ Path.Buffer = Vcb->Name.Buffer + 5;
+ Path.Length = Vcb->Name.Length - 10;
+ } else {
+ Path = Vcb->Name;
+ }
+
+ // Strip off the unicode prefix
+
+ Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
+ Path.Length -= Vcb->Scb->UnicodeUid.Length;
+ Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
+
+ if (OutputBufferLength < Path.Length + 2 * sizeof(WCHAR)) {
+ InputBuffer->Parameters.GetConn.BytesNeeded =
+ Path.Length + 2 * sizeof(WCHAR);
+ try_return( Status = STATUS_BUFFER_TOO_SMALL );
+ }
+
+ //
+ // Return the Connection name in the form \\server\share<NUL>
+ //
+
+ OutputBuffer->UncName[0] = L'\\';
+
+ RtlMoveMemory(
+ &OutputBuffer->UncName[1],
+ Path.Buffer,
+ Path.Length );
+
+ OutputBuffer->UncName[ (Path.Length + sizeof(WCHAR)) / sizeof(WCHAR) ] = L'\0';
+
+ Irp->IoStatus.Information = Path.Length + 2 * sizeof(WCHAR);
+
+try_exit: NOTHING;
+
+ } finally {
+ NOTHING;
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+DeleteConnection(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns removes a connection if force is specified or
+ if there are no open handles on this Vcb.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ PICB Icb;
+ PVCB Vcb;
+ PDCB Dcb;
+ PNONPAGED_DCB NonPagedDcb;
+ NODE_TYPE_CODE NodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "DeleteConnection...\n", 0);
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Wait to get to the head of the SCB queue. We do this in case
+ // we need to disconnect, so that we can send packets with the RCB
+ // resource held.
+ //
+
+ NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, &NonPagedDcb, &Icb );
+
+ if ( NodeTypeCode == NW_NTC_ICB_SCB ) {
+ IrpContext->pNpScb = Icb->SuperType.Scb->pNpScb;
+ } else {
+ ASSERT( NodeTypeCode == NW_NTC_ICB );
+ IrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb;
+ Dcb = NonPagedDcb->Fcb;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Acquire exclusive access to the RCB.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed, and that it is a directory handle.
+ //
+
+ if ( NodeTypeCode == NW_NTC_ICB_SCB ) {
+
+
+ if ( Icb->IsTreeHandle ) {
+
+ //
+ // Do an NDS logoff. This will release the RCB.
+ //
+
+ Status = NdsLogoff( IrpContext );
+ DebugTrace( 0, Dbg, "Nds tree logoff -> %08lx\n", Status );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Delete connection to SCB %X\n", Icb->SuperType.Scb );
+
+ Status = TreeDisconnectScb( IrpContext, Icb->SuperType.Scb );
+ DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status );
+
+ }
+
+ try_return( NOTHING );
+
+ } else if ( NodeTypeCode != NW_NTC_ICB ||
+ Dcb == NULL ||
+ ( Dcb->NodeTypeCode != NW_NTC_DCB &&
+ Dcb->NodeTypeCode != NW_NTC_FCB) ) {
+
+ DebugTrace(0, Dbg, "Invalid file handle\n", 0);
+
+ Status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ Vcb = Dcb->Vcb;
+ DebugTrace(0, Dbg, "Attempt to delete VCB = %08lx\n", Vcb);
+
+ //
+ // Vcb->OpenFileCount will be 1, (to account for this DCB), if the
+ // connection can be deleted.
+ //
+
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) ) {
+ DebugTrace(0, Dbg, "Cannot delete unredireced connection\n", 0);
+ try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
+ } else {
+
+ if ( Vcb->OpenFileCount > 1 ) {
+ DebugTrace(0, Dbg, "Cannot delete in use connection\n", 0);
+ Status = STATUS_CONNECTION_IN_USE;
+ } else {
+
+ //
+ // To delete the VCB, simply dereference it.
+ //
+
+ DebugTrace(0, Dbg, "Deleting connection\n", 0);
+
+ ClearFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ --Vcb->Scb->OpenFileCount;
+
+ NwDereferenceVcb( Vcb, IrpContext, TRUE );
+ }
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+
+ //
+ // An NDS logoff will have already freed the RCB
+ // and dequeued the irp context.
+ //
+
+ if ( ! ( Icb->IsTreeHandle ) ) {
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+EnumConnections(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the list of redirector connections.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP Context block for this request.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_SERVER_RESOURCE OutputBuffer;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PVCB Vcb;
+ PSCB Scb;
+ BOOLEAN OwnRcb;
+
+ UNICODE_STRING LocalName;
+ UNICODE_STRING ContainerName;
+ PCHAR FixedPortion;
+ PWCHAR EndOfVariableData;
+ ULONG EntrySize;
+ ULONG ResumeKey;
+
+ ULONG ShareType;
+ ULONG EntriesRead = 0;
+ ULONG EntriesRequested;
+ DWORD ConnectionType;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING Path;
+
+ DebugTrace(0, Dbg, "EnumConnections...\n", 0);
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.EnumConn.BytesNeeded ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer;
+ OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Acquire shared access to the drive map table.
+ //
+
+ NwAcquireSharedRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ //
+ // Initialize returned strings
+ //
+
+ RtlInitUnicodeString( &ContainerName, L"\\" );
+
+ FixedPortion = (PCHAR) OutputBuffer;
+ EndOfVariableData = (PWCHAR) ((ULONG) FixedPortion + OutputBufferLength);
+ ConnectionType = InputBuffer->Parameters.EnumConn.ConnectionType;
+
+ EntriesRequested = InputBuffer->Parameters.EnumConn.EntriesRequested;
+
+ //
+ // Run through the global VCB list looking for redirections.
+ //
+
+ ResumeKey = InputBuffer->Parameters.EnumConn.ResumeKey;
+
+ DebugTrace(0, Dbg, "Starting resume key is %d\n", InputBuffer->Parameters.EnumConn.ResumeKey );
+
+ for ( ListEntry = GlobalVcbList.Flink;
+ ListEntry != &GlobalVcbList &&
+ EntriesRequested > EntriesRead &&
+ Status == STATUS_SUCCESS ;
+ ListEntry = ListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry );
+
+ //
+ // Skip connections that we've already enumerated.
+ //
+
+ if ( Vcb->SequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) {
+ continue;
+ }
+
+ //
+ // Skip implicit connections, if they are not requested.
+ //
+
+ if ( !(ConnectionType & CONNTYPE_IMPLICIT) &&
+ !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) {
+
+ continue;
+ }
+
+ //
+ // Skip connections that are not requested.
+ //
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
+ if ( !( ConnectionType & CONNTYPE_PRINT ))
+ continue;
+ } else {
+ if ( !( ConnectionType & CONNTYPE_DISK ))
+ continue;
+ }
+
+
+ if ( Vcb->DriveLetter != 0 ) {
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
+ RtlInitUnicodeString( &LocalName, L"LPT1" );
+ LocalName.Buffer[3] = Vcb->DriveLetter;
+ ShareType = RESOURCETYPE_PRINT;
+ } else {
+ RtlInitUnicodeString( &LocalName, L"A:" );
+ LocalName.Buffer[0] = Vcb->DriveLetter;
+ ShareType = RESOURCETYPE_DISK;
+ }
+ } else { // No drive letter connection, i.e. UNC Connection
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ))
+ ShareType = RESOURCETYPE_PRINT;
+ else
+ ShareType = RESOURCETYPE_DISK;
+ }
+
+ if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
+ Path.Buffer = Vcb->Name.Buffer + 3;
+ Path.Length = Vcb->Name.Length - 6;
+ } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
+ Path.Buffer = Vcb->Name.Buffer + 5;
+ Path.Length = Vcb->Name.Length - 10;
+ } else {
+ Path = Vcb->Name;
+ }
+
+ // Strip off the unicode prefix
+
+ Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
+ Path.Length -= Vcb->Scb->UnicodeUid.Length;
+ Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
+
+ Status = WriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ &ContainerName,
+ Vcb->DriveLetter != 0 ? &LocalName : NULL,
+ &Path,
+ RESOURCE_CONNECTED,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ ShareType,
+ &EntrySize
+ );
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize;
+
+ } else if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+
+ EntriesRead++;
+ ResumeKey = Vcb->SequenceNumber;
+
+ DebugTrace(0, Dbg, "Returning VCB %08lx\n", Vcb );
+ DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey );
+ }
+ }
+
+ //
+ // Return the Servers we are connected to. This is most important for
+ // support of NetWare aware 16 bit apps.
+ //
+
+ if ((ConnectionType & CONNTYPE_IMPLICIT) &&
+ ( ConnectionType & CONNTYPE_DISK )) {
+
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNpScb;
+ PLIST_ENTRY NextScbQueueEntry;
+ ULONG EnumSequenceNumber = 0x80000000;
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ RtlInitUnicodeString( &ContainerName, L"\\\\" );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for ( ListEntry = ScbQueue.Flink;
+ ListEntry != &ScbQueue &&
+ EntriesRequested > EntriesRead &&
+ Status == STATUS_SUCCESS ;
+ ListEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks );
+ Scb = pNpScb->pScb;
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Skip connections that we've already enumerated.
+ //
+
+ if (( EnumSequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) ||
+
+ ( pNpScb == &NwPermanentNpScb ) ||
+
+ (( pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) &&
+ ( pNpScb->State != SCB_STATE_IN_USE ))) {
+
+ //
+ // Move to next entry in the list
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ EnumSequenceNumber++;
+ continue;
+ }
+
+ DebugTrace( 0, Dbg, " EnumConnections returning Servername = %wZ\n", &pNpScb->ServerName );
+
+ Status = WriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ &ContainerName,
+ NULL,
+ &pNpScb->ServerName,
+ RESOURCE_CONNECTED,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ RESOURCETYPE_DISK,
+ &EntrySize
+ );
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize;
+
+ } else if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+
+ EntriesRead++;
+ ResumeKey = EnumSequenceNumber;
+
+ DebugTrace(0, Dbg, "Returning SCB %08lx\n", Scb );
+ DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey );
+ }
+
+ //
+ // Move to next entry in the list
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ EnumSequenceNumber++;
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+ }
+
+ InputBuffer->Parameters.EnumConn.EntriesReturned = EntriesRead;
+ InputBuffer->Parameters.EnumConn.ResumeKey = ResumeKey;
+
+ if ( EntriesRead == 0 ) {
+
+ if (Status == STATUS_SUCCESS) {
+ Status = STATUS_NO_MORE_ENTRIES;
+ }
+
+ Irp->IoStatus.Information = 0;
+ }
+ else {
+ Irp->IoStatus.Information = OutputBufferLength;
+ }
+
+ } finally {
+ if (OwnRcb) {
+ NwReleaseRcb( &NwRcb );
+ }
+ }
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+WriteNetResourceEntry(
+ IN OUT PCHAR *FixedPortion,
+ IN OUT PWCHAR *EndOfVariableData,
+ IN PUNICODE_STRING ContainerName OPTIONAL,
+ IN PUNICODE_STRING LocalName OPTIONAL,
+ IN PUNICODE_STRING RemoteName,
+ IN ULONG ScopeFlag,
+ IN ULONG DisplayFlag,
+ IN ULONG UsageFlag,
+ IN ULONG ShareType,
+ OUT PULONG EntrySize
+ )
+/*++
+
+Routine Description:
+
+ This function packages a NETRESOURCE entry into the user output buffer.
+
+Arguments:
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a NETRESOURCE entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the
+ user information is written into the output buffer starting from
+ the end.
+
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ ContainerName - Supplies the full path qualifier to make RemoteName
+ a full UNC name.
+
+ LocalName - Supplies the local device name, if any.
+
+ RemoteName - Supplies the remote resource name.
+
+ ScopeFlag - Supplies the flag which indicates whether this is a
+ CONNECTED or GLOBALNET resource.
+
+ DisplayFlag - Supplies the flag which tells the UI how to display
+ the resource.
+
+ UsageFlag - Supplies the flag which indicates that the RemoteName
+ is either a container or a connectable resource or both.
+
+ ShareType - Type of the share connected to, RESOURCETYPE_PRINT or
+ RESOURCETYPE_DISK
+
+ EntrySize - Receives the size of the NETRESOURCE entry in bytes.
+
+Return Value:
+
+ STATUS_SUCCESS - Successfully wrote entry into user buffer.
+
+ STATUS_NO_MEMORY - Failed to allocate work buffer.
+
+ STATUS_MORE_ENTRIES - Buffer was too small to fit entry.
+
+--*/
+{
+ BOOLEAN FitInBuffer = TRUE;
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion;
+ UNICODE_STRING TmpRemote;
+
+ PAGED_CODE();
+
+ *EntrySize = sizeof(NETRESOURCEW) +
+ RemoteName->Length + NwProviderName.Length + 2 * sizeof(WCHAR);
+
+ if (ARGUMENT_PRESENT(LocalName)) {
+ *EntrySize += LocalName->Length + sizeof(WCHAR);
+ }
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ *EntrySize += ContainerName->Length;
+ }
+
+ //
+ // See if buffer is large enough to fit the entry.
+ //
+ if (((ULONG) *FixedPortion + *EntrySize) >
+ (ULONG) *EndOfVariableData) {
+
+ return STATUS_MORE_ENTRIES;
+ }
+
+ NetR->dwScope = ScopeFlag;
+ NetR->dwType = ShareType;
+ NetR->dwDisplayType = DisplayFlag;
+ NetR->dwUsage = UsageFlag;
+ NetR->lpComment = NULL;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (ULONG) (*FixedPortion) += sizeof(NETRESOURCEW);
+
+ //
+ // RemoteName
+ //
+ if (ARGUMENT_PRESENT(ContainerName)) {
+
+ //
+ // Prefix the RemoteName with its container name making the
+ // it a fully-qualified UNC name.
+ //
+
+ TmpRemote.MaximumLength = RemoteName->Length + ContainerName->Length + sizeof(WCHAR);
+ TmpRemote.Buffer = ALLOCATE_POOL(
+ PagedPool,
+ RemoteName->Length + ContainerName->Length + sizeof(WCHAR)
+ );
+
+ if (TmpRemote.Buffer == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlCopyUnicodeString(&TmpRemote, ContainerName);
+ RtlAppendUnicodeStringToString(&TmpRemote, RemoteName);
+ }
+ else {
+ TmpRemote = *RemoteName;
+ }
+
+ FitInBuffer = CopyStringToBuffer(
+ TmpRemote.Buffer,
+ TmpRemote.Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpRemoteName
+ );
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ FREE_POOL(TmpRemote.Buffer);
+ }
+
+ ASSERT(FitInBuffer);
+
+ //
+ // LocalName
+ //
+ if (ARGUMENT_PRESENT(LocalName)) {
+ FitInBuffer = CopyStringToBuffer(
+ LocalName->Buffer,
+ LocalName->Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpLocalName
+ );
+
+ ASSERT(FitInBuffer);
+ }
+ else {
+ NetR->lpLocalName = NULL;
+ }
+
+ //
+ // ProviderName
+ //
+
+ FitInBuffer = CopyStringToBuffer(
+ NwProviderName.Buffer,
+ NwProviderName.Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpProvider
+ );
+
+ ASSERT(FitInBuffer);
+
+ if (! FitInBuffer) {
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+BOOL
+CopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This is based on ..\nwlib\NwlibCopyStringToBuffer
+
+ This routine puts a single variable-length string into an output buffer.
+ The string is not written if it would overwrite the last fixed structure
+ in the buffer.
+
+Arguments:
+
+ SourceString - Supplies a pointer to the source string to copy into the
+ output buffer. If SourceString is null then a pointer to a zero terminator
+ is inserted into output buffer.
+
+ CharacterCount - Supplies the length of SourceString, not including zero
+ terminator. (This in units of characters - not bytes).
+
+ FixedDataEnd - Supplies a pointer to just after the end of the last
+ fixed structure in the buffer.
+
+ EndOfVariableData - Supplies an address to a pointer to just after the
+ last position in the output buffer that variable data can occupy.
+ Returns a pointer to the string written in the output buffer.
+
+ VariableDataPointer - Supplies a pointer to the place in the fixed
+ portion of the output buffer where a pointer to the variable data
+ should be written.
+
+Return Value:
+
+ Returns TRUE if string fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ DWORD CharsNeeded = (CharacterCount + 1);
+
+ PAGED_CODE();
+
+ //
+ // Determine if source string will fit, allowing for a zero terminator.
+ // If not, just set the pointer to NULL.
+ //
+
+ if ((*EndOfVariableData - CharsNeeded) >= FixedDataEnd) {
+
+ //
+ // It fits. Move EndOfVariableData pointer up to the location where
+ // we will write the string.
+ //
+
+ *EndOfVariableData -= CharsNeeded;
+
+ //
+ // Copy the string to the buffer if it is not null.
+ //
+
+ if (CharacterCount > 0 && SourceString != NULL) {
+
+ (VOID) wcsncpy(*EndOfVariableData, SourceString, CharacterCount);
+ }
+
+ //
+ // Set the zero terminator.
+ //
+
+ *(*EndOfVariableData + CharacterCount) = L'\0';
+
+ //
+ // Set up the pointer in the fixed data portion to point to where the
+ // string is written.
+ //
+
+ *VariableDataPointer = *EndOfVariableData;
+
+ return TRUE;
+
+ }
+ else {
+
+ //
+ // It doesn't fit. Set the offset to NULL.
+ //
+
+ *VariableDataPointer = NULL;
+
+ return FALSE;
+ }
+}
+
+
+NTSTATUS
+GetRemoteHandle(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the NetWare handle for a Directory. This is used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PCHAR OutputBuffer;
+ PICB Icb;
+ PDCB Dcb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetRemoteHandle\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ }
+
+ Dcb = (PDCB)Icb->SuperType.Fcb;
+ nodeTypeCode = Dcb->NodeTypeCode;
+
+ if ( nodeTypeCode != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+#if 1
+ if ( nodeTypeCode != NW_NTC_FCB ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Return the 6 byte NetWare handle for this file.
+ //
+
+ if (!Icb->HasRemoteHandle) {
+
+ Status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ if ( OutputBufferLength < sizeof( UCHAR ) ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ RtlCopyMemory( OutputBuffer, Icb->Handle, 6 * sizeof(CHAR));
+ IrpContext->pOriginalIrp->IoStatus.Information = 6 * sizeof(CHAR);
+
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+#else
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+#endif
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ if ( OutputBufferLength < sizeof( UCHAR ) ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if ( Icb->HasRemoteHandle ) {
+
+ // Already been asked for the handle
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ *OutputBuffer = Icb->Handle[0];
+
+ IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR);
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ CHAR Handle;
+
+ IrpContext->pScb = Dcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE,
+ Dcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Dcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Handle );
+
+ if (NT_SUCCESS(Status)) {
+ *OutputBuffer = Handle;
+ Icb->Handle[0] = Handle;
+ Icb->HasRemoteHandle = TRUE;
+ IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR);
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ DebugTrace( 0, Dbg, " -> %02x\n", Handle );
+
+ }
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetUserName(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the UserName that would be used to connect to a particular
+ server.
+
+ If there are credentials specific to this connection use them
+ otherwise use the logon credentials.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PWSTR InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PWSTR OutputBuffer;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING UidServer;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING ConvertedName;
+ PUNICODE_STRING pUserName;
+ PSCB pScb;
+ PLOGON pLogon;
+ BOOLEAN CredentialsHeld = FALSE;
+ BOOLEAN FailedTreeLookup = FALSE;
+ PNDS_SECURITY_CONTEXT pNdsCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetUserName\n", 0);
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ ServerName.Buffer = InputBuffer;
+ ServerName.MaximumLength = (USHORT)InputBufferLength;
+ ServerName.Length = (USHORT)InputBufferLength;
+ Status = MakeUidServer( &UidServer, &Uid, &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status );
+ return(Status);
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer );
+
+ //
+ // Get the login for this user.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE);
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // First try this name as a server. Avoid FindScb creating a
+ // connection to the server if one doesn't exist already.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT );
+ NwFindScb( &pScb, IrpContext, &UidServer, &ServerName );
+
+ pUserName = NULL;
+
+ //
+ // Look for bindery server name, or tree login name.
+ //
+
+ if ( pScb != NULL ) {
+
+ if ( pScb->UserName.Buffer != NULL ) {
+
+ pUserName = &pScb->UserName;
+
+ } else if ( pScb->NdsTreeName.Buffer != NULL &&
+ pScb->NdsTreeName.Length > 0 ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ CredentialsHeld = TRUE;
+
+ if ( pNdsCredentials->Credential ) {
+
+ //
+ // If we have login data, get the user name.
+ //
+
+ ConvertedName.Length = pNdsCredentials->Credential->userNameLength -
+ sizeof( WCHAR );
+ ConvertedName.MaximumLength = ConvertedName.Length;
+ ConvertedName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsCredentials->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsCredentials->Credential->optDataSize );
+
+ pUserName = &ConvertedName;
+
+ } else {
+
+ //
+ // If there's no credential data, we're not logged in.
+ //
+
+ FailedTreeLookup = TRUE;
+ }
+
+ } else {
+
+ FailedTreeLookup = TRUE;
+ }
+
+ }
+
+ }
+
+ //
+ // If it wasn't a server and we haven't already tried a tree, do so now.
+ //
+
+ if ( pUserName == NULL &&
+ !FailedTreeLookup ) {
+
+ Status = NdsLookupCredentials( &ServerName,
+ pLogon,
+ &pNdsCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ CredentialsHeld = TRUE;
+
+ if ( pNdsCredentials->Credential ) {
+
+ //
+ // If we've logged in, get the user name.
+ //
+
+ ConvertedName.Length = pNdsCredentials->Credential->userNameLength -
+ sizeof( WCHAR );
+ ConvertedName.MaximumLength = ConvertedName.Length;
+ ConvertedName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsCredentials->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsCredentials->Credential->optDataSize );
+
+ pUserName = &ConvertedName;
+
+ }
+ }
+
+ }
+
+ //
+ // If we still don't know, return the default name.
+ //
+
+ if ( pUserName == NULL &&
+ pLogon != NULL ) {
+
+ pUserName = &pLogon->UserName;
+ }
+
+ FREE_POOL(UidServer.Buffer);
+
+ if ( pUserName ) {
+
+ DebugTrace( 0, Dbg, "Get User Name: %wZ\n", pUserName );
+
+ try {
+
+ if (pUserName->Length > OutputBufferLength) {
+
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ReleaseAndExit;
+ }
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ IrpContext->pOriginalIrp->IoStatus.Information = pUserName->Length;
+ RtlMoveMemory( OutputBuffer, pUserName->Buffer, pUserName->Length);
+
+ Status = STATUS_SUCCESS;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ReleaseAndExit:
+
+ if ( pScb ) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status );
+
+ if ( CredentialsHeld ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+GetChallenge(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds the challenge and session key for rpc using the
+ credentials stored in the redirector. The Rpc client can supply a
+ password. This allows the redirector to keep the algorithm in one
+ place.
+
+ If a password is supplied then use that, if there is a password on this
+ specific connection use that, otherwise use the logon credentials.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_GET_CHALLENGE_REQUEST InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_CHALLENGE_REPLY OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ OEM_STRING Password;
+ PSCB pScb;
+ PLOGON pLogon;
+ BOOLEAN RcbHeld = FALSE;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER ProcessUid;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetChallenge\n", 0);
+
+ if ((InputBufferLength <
+ (FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])) +
+ InputBuffer->ServerNameorPasswordLength)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Only allow processes running in the system context to call this api to prevent
+ // password attacks.
+ //
+ SeCaptureSubjectContext(&SubjectContext);
+ SeQueryAuthenticationIdToken(&SubjectContext.PrimaryToken, (PLUID)&ProcessUid);
+ SeReleaseSubjectContext(&SubjectContext);
+
+ // FIXFIX surely there's a define for 3e7 somewhere.
+
+ if (ProcessUid.QuadPart != 0x3e7) {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Password.Buffer = NULL;
+
+ if ( InputBuffer->Flags == CHALLENGE_FLAGS_SERVERNAME ) {
+
+ PUNICODE_STRING pPassword;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING UidServer;
+
+ if (InputBuffer->ServerNameorPasswordLength == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We have to supply the password from the redirector
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ ServerName.Buffer = (PWSTR)((PUCHAR)InputBuffer +
+ FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]));
+ ServerName.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength;
+ ServerName.Length = (USHORT)InputBuffer->ServerNameorPasswordLength;
+
+ Status = MakeUidServer( &UidServer, &Uid, &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return(Status);
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer );
+
+ //
+ // Avoid FindScb creating a connection to the server if one
+ // doesn't exist already.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT );
+ NwFindScb( &pScb, IrpContext, &UidServer, &ServerName );
+
+ try {
+
+ if ((pScb != NULL) &&
+ (pScb->Password.Buffer != NULL)) {
+
+ pPassword = &pScb->Password;
+
+ } else {
+
+ //
+ // Use default credentials for this UID
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ RcbHeld = TRUE;
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE);
+
+ if (pLogon != NULL ) {
+
+ pPassword = &pLogon->PassWord;
+
+ } else {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", STATUS_ACCESS_DENIED );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+ if (pPassword->Length != 0) {
+ Status = RtlUpcaseUnicodeStringToOemString( &Password, pPassword, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return( Status );
+ }
+ } else {
+ Password.Buffer = "";
+ Password.Length = Password.MaximumLength = 0;
+ }
+
+ } finally {
+
+ if (RcbHeld) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if (pScb != NULL) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ FREE_POOL(UidServer.Buffer);
+ }
+
+ } else {
+
+ UNICODE_STRING LocalPassword;
+
+ LocalPassword.Buffer = (PWSTR)((PUCHAR)InputBuffer +
+ FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]));
+ LocalPassword.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength;
+ LocalPassword.Length = (USHORT)InputBuffer->ServerNameorPasswordLength;
+
+ if (LocalPassword.Length != 0) {
+ Status = RtlUpcaseUnicodeStringToOemString( &Password, &LocalPassword, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return( Status );
+ }
+ } else {
+ Password.Buffer = "";
+ Password.Length = Password.MaximumLength = 0;
+ }
+ }
+
+ DebugTrace( 0, Dbg, " ->Password = \"%Z\"\n", &Password );
+
+ try {
+ RespondToChallenge( (PUCHAR)&InputBuffer->ObjectId, &Password, InputBuffer->Challenge, OutputBuffer->Challenge);
+
+ } finally {
+
+ if ( Password.Length > 0 ) {
+
+ RtlFreeAnsiString( &Password );
+ }
+ }
+
+ Irp->IoStatus.Information = sizeof(NWR_GET_CHALLENGE_REPLY);
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return Status;
+}
+
+NTSTATUS
+WriteConnStatusEntry(
+ PSCB pConnectionScb,
+ PBYTE pbUserBuffer,
+ DWORD dwBufferLen,
+ DWORD *pdwBytesWritten,
+ DWORD *pdwBytesNeeded,
+ BOOLEAN fCallerScb
+ )
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ BOOLEAN fHoldingCredentials = FALSE;
+ PUNICODE_STRING puUserName = NULL;
+ UNICODE_STRING CredentialName;
+ UNICODE_STRING ServerName;
+ PCONN_STATUS pStatus;
+ DWORD dwBytesNeeded;
+ PBYTE pbStrPtr;
+ DWORD dwAllowedHandles;
+
+ //
+ // If this is an NDS connection, get the credentials.
+ //
+
+ if ( ( pConnectionScb->MajorVersion > 3 ) &&
+ ( pConnectionScb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pConnectionScb->UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &(pConnectionScb->NdsTreeName),
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ fHoldingCredentials = TRUE;
+
+ if ( pNdsContext->Credential != NULL ) {
+
+ CredentialName.Length = pNdsContext->Credential->userNameLength -
+ sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ puUserName = &CredentialName;
+ }
+
+ }
+ }
+
+ } else {
+
+ if ( pConnectionScb->UserName.Length != 0 ) {
+ puUserName = &(pConnectionScb->UserName);
+ } else {
+ puUserName = NULL;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "WriteConnStatus: UserName %wZ\n", puUserName );
+
+ //
+ // Strip off the uid from the server name.
+ //
+
+ ServerName.Length = (pConnectionScb->UidServerName).Length;
+ ServerName.Buffer = (pConnectionScb->UidServerName).Buffer;
+
+ while ( ServerName.Length ) {
+
+ if ( ServerName.Buffer[0] == L'\\' ) {
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+ break;
+ }
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+
+ }
+
+ DebugTrace( 0, Dbg, "WriteConnStatus: ServerName %wZ\n", &ServerName );
+
+ //
+ // Do we have enough space? Don't forget that we have to
+ // NULL terminate the WCHAR strings.
+ //
+
+ dwBytesNeeded = sizeof( CONN_STATUS );
+
+ dwBytesNeeded += ( ServerName.Length + sizeof( WCHAR ) );
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+ dwBytesNeeded += ( pConnectionScb->NdsTreeName.Length + sizeof( WCHAR ) );
+ }
+
+ if ( puUserName ) {
+ dwBytesNeeded += ( puUserName->Length + sizeof( WCHAR ) );
+ }
+
+ //
+ // Pad the end to make sure all structures are aligned.
+ //
+
+ dwBytesNeeded = ROUNDUP4( dwBytesNeeded );
+
+ if ( dwBytesNeeded > dwBufferLen ) {
+
+ *pdwBytesNeeded = dwBytesNeeded;
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Fill in the CONN_STATUS structure.
+ //
+
+ try {
+
+ pStatus = (PCONN_STATUS)pbUserBuffer;
+ pbStrPtr = pbUserBuffer + sizeof( CONN_STATUS );
+
+ //
+ // We always have a server name.
+ //
+
+ pStatus->pszServerName = (PWSTR) pbStrPtr;
+ pbStrPtr += ( ServerName.Length + sizeof( WCHAR ) );
+
+ //
+ // Fill in the user name if applicable.
+ //
+
+ if ( puUserName ) {
+
+ pStatus->pszUserName = (PWSTR) pbStrPtr;
+ pbStrPtr += ( puUserName->Length + sizeof( WCHAR ) );
+
+ } else {
+
+ pStatus->pszUserName = NULL;
+ }
+
+ //
+ // Fill in the tree name if applicable.
+ //
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+
+ pStatus->pszTreeName = (PWSTR) pbStrPtr;
+
+ } else {
+
+ pStatus->pszTreeName = NULL;
+ }
+
+ //
+ // Fill in the connection number if applicable.
+ //
+
+ if ( ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) ||
+ ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ) {
+
+ pStatus->nConnNum = (DWORD)(pConnectionScb->pNpScb->ConnectionNo);
+
+ } else {
+
+ pStatus->nConnNum = 0;
+
+ }
+
+ //
+ // Copy the user name over.
+ //
+
+ if ( puUserName ) {
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszUserName),
+ (PBYTE)(puUserName->Buffer),
+ puUserName->Length );
+ *(pStatus->pszUserName + (puUserName->Length / sizeof( WCHAR ))) = L'\0';
+
+ }
+
+ //
+ // Set the NDS flag and authentication fields.
+ //
+
+ if ( ( pConnectionScb->MajorVersion > 3 ) &&
+ ( pConnectionScb->UserName.Length == 0 ) ) {
+
+ pStatus->fNds = TRUE;
+
+ if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ if ( ( pConnectionScb->VcbCount ) || ( pConnectionScb->OpenNdsStreams ) ) {
+ pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_LICENSED;
+ } else {
+ pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_NO_LICENSE;
+ }
+
+ } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+
+ pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED;
+
+ } else {
+
+ pStatus->dwConnType = NW_CONN_DISCONNECTED;
+
+ }
+
+ } else {
+
+ pStatus->fNds = FALSE;
+
+ if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ pStatus->dwConnType = NW_CONN_BINDERY_LOGIN;
+
+ } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+
+ pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED;
+
+ } else {
+
+ pStatus->dwConnType = NW_CONN_DISCONNECTED;
+
+ }
+
+ }
+
+ //
+ // Copy over the tree name.
+ //
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszTreeName),
+ (PBYTE)(pConnectionScb->NdsTreeName.Buffer),
+ pConnectionScb->NdsTreeName.Length );
+ *( pStatus->pszTreeName +
+ ( pConnectionScb->NdsTreeName.Length / sizeof( WCHAR ) ) ) = L'\0';
+
+ } else {
+
+ pStatus->pszTreeName = NULL;
+ }
+
+ //
+ // Copy the server name over.
+ //
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszServerName),
+ (PBYTE)(ServerName.Buffer),
+ ServerName.Length );
+ *(pStatus->pszServerName + (ServerName.Length / sizeof( WCHAR ))) = L'\0';
+
+ //
+ // Set the preferred server field if this is a preferred server
+ // and there are no explicit uses for the connection. If the
+ // fCallerScb parameter is TRUE, then this SCB has a handle from
+ // the caller of the API and we have to make an allowance for
+ // that handle. Yes, this is kind of ugly.
+ //
+
+ if ( fCallerScb ) {
+ dwAllowedHandles = 1;
+ } else {
+ dwAllowedHandles = 0;
+ }
+
+ if ( ( pConnectionScb->PreferredServer ) &&
+ ( pConnectionScb->OpenFileCount == 0 ) &&
+ ( pConnectionScb->IcbCount == dwAllowedHandles ) ) {
+
+ pStatus->fPreferred = TRUE;
+
+ } else {
+
+ pStatus->fPreferred = FALSE;
+ }
+
+ //
+ // Fill out the length.
+ //
+
+ pStatus->dwTotalLength = dwBytesNeeded;
+ *pdwBytesWritten = dwBytesNeeded;
+ Status = STATUS_SUCCESS;
+
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ DebugTrace( 0, Dbg, "Exception %08lx accessing user mode buffer.\n", Status );
+ goto ExitWithCleanup;
+
+ }
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentials ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetConnStatus(
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_OBJECT FileObject
+ )
+/*++
+
+ Get the connection status for the described connection.
+ The following connection requests are valid:
+
+ Server (e.g. "MARS312") - returns a single connection
+ status structure for this server if the user has a
+ connection to the server.
+
+ Tree (e.g. "*MARSDEV") - returns a connection status
+ structure for every server in the tree that the user
+ has a connection to.
+
+ All Connections (e.g. "") - returns a connection status
+ structure for every server that the user has a
+ connection to.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ BYTE *OutputBuffer;
+ ULONG OutputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING ConnectionName, UidServer;
+ BOOL fTreeConnections = FALSE;
+ BOOL fServerConnection = FALSE;
+ BOOL OwnRcb = FALSE;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ DWORD dwBytesWritten, dwBytesNeeded;
+ KIRQL OldIrql;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ DWORD dwReturned = 0;
+ ULONG SequenceNumber = 0;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB pIcb;
+ PSCB pCallerScb;
+ PVOID fsContext, fsContext2;
+
+ //
+ // Get the appropriate buffers.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ //
+ // Figure out who this request applies to.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ RtlInitUnicodeString( &ConnectionName, NULL );
+ RtlInitUnicodeString( &UidServer, NULL );
+
+ //
+ // Figure out who the caller of this routine is so we know to
+ // ignore their handle when deciding what to return.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 );
+
+ if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ pIcb = (PICB) fsContext2;
+ pCallerScb = pIcb->SuperType.Scb;
+ DebugTrace( 0, Dbg, "GetConnStatus called by handle on %08lx\n", pCallerScb );
+
+ } else {
+
+ pCallerScb = NULL;
+ DebugTrace( 0, Dbg, "Couldn't figure out who called us.\n", 0 );
+ }
+
+ //
+ //
+ // Figure out which connections we're looking for.
+ //
+
+ try {
+
+ if ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength != 0 ) {
+
+ if ( InputBuffer->Parameters.GetConnStatus.ConnectionName[0] == L'*' ) {
+
+ ConnectionName.Buffer = &(InputBuffer->Parameters.GetConnStatus.ConnectionName[1]);
+ ConnectionName.Length = (USHORT)
+ ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength -
+ sizeof( WCHAR ) );
+ ConnectionName.MaximumLength = ConnectionName.Length;
+
+ fTreeConnections = TRUE;
+
+ DebugTrace( 0, Dbg, "GetConnStatus: Tree is %wZ\n", &ConnectionName );
+
+ } else {
+
+ ConnectionName.Buffer = InputBuffer->Parameters.GetConnStatus.ConnectionName;
+ ConnectionName.Length = (USHORT)
+ (InputBuffer->Parameters.GetConnStatus.ConnectionNameLength);
+ ConnectionName.MaximumLength = ConnectionName.Length;
+
+ fServerConnection = TRUE;
+
+ Status = MakeUidServer( &UidServer, &Uid, &ConnectionName );
+ if ( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnStatus: Server is %wZ\n", &UidServer );
+ }
+
+ } else {
+
+ DebugTrace( 0, Dbg, "GetConnectionStatus: Enumerate all connections.\n", 0 );
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad input buffer in GetConnStatus.\n" , 0 );
+
+ }
+
+ //
+ // If this is a server connection, find and return it.
+ //
+
+ if ( fServerConnection ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServer, 0 );
+
+ if ( !PrefixEntry ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+
+ if ( ( pScb->PreferredServer ) ||
+ ( pScb->OpenFileCount > 0 ) ) {
+
+ //
+ // If there are open files, we need to return this.
+ // We always write status entries for the preferred
+ // server so that we can give default logon info.
+ //
+
+ goto ProcessServer;
+ }
+
+ //
+ // Are there open handles other than the caller?
+ //
+
+ if ( pScb == pCallerScb ) {
+
+ if ( pScb->IcbCount > 1 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 1 );
+ goto ProcessServer;
+ }
+
+ } else {
+
+ if ( pScb->IcbCount > 0 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 0 );
+ goto ProcessServer;
+ }
+ }
+
+ //
+ // Not an explicit use for this server.
+ //
+ goto ExitWithCleanup;
+
+ProcessServer:
+
+ NwReferenceScb( pScb->pNpScb );
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ Status = WriteConnStatusEntry( pScb,
+ OutputBuffer,
+ OutputBufferLength,
+ &dwBytesWritten,
+ &dwBytesNeeded,
+ (BOOLEAN)( pScb == pCallerScb ) );
+
+ NwDereferenceScb( pScb->pNpScb );
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = 0;
+
+ if ( !NT_SUCCESS( Status )) {
+
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = 0;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = dwBytesNeeded;
+ Irp->IoStatus.Information = 0;
+ goto ExitWithCleanup;
+
+ } else {
+
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = 1;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0;
+ Irp->IoStatus.Information = dwBytesWritten;
+ goto ExitWithCleanup;
+
+ }
+ }
+
+ //
+ // We want all connections or all tree connections, so
+ // we need to walk the list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = ScbQueue.Flink;
+
+ while ( ListEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks );
+ pScb = pNpScb->pScb;
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Make sure we pass up the one's we've already returned.
+ //
+
+ if ( ( SequenceNumber >= InputBuffer->Parameters.GetConnStatus.ResumeKey ) &&
+ ( pNpScb != &NwPermanentNpScb ) ) {
+
+ //
+ // If there are open files, we need to return this.
+ // We always write status entries for the preferred
+ // server so that we can give default logon info.
+ //
+
+ if ( ( pScb->PreferredServer ) ||
+ ( pScb->OpenFileCount > 0 ) ) {
+ goto SecondProcessServer;
+ }
+
+ //
+ // Are there any handles other than the caller?
+ //
+
+ if ( pScb == pCallerScb ) {
+
+ if ( pScb->IcbCount > 1 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 2 );
+ goto SecondProcessServer;
+ }
+
+ } else {
+
+ if ( pScb->IcbCount > 0 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 1 );
+ goto SecondProcessServer;
+ }
+ }
+
+ }
+
+ //
+ // Not an interesting server; move to next entry.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ continue;
+
+SecondProcessServer:
+
+ //
+ // We have a possible candidate; see if the uid and tree are appropriate.
+ //
+
+ if ( ( (pScb->UserUid).QuadPart != Uid.QuadPart ) ||
+
+ ( fTreeConnections &&
+ !RtlEqualUnicodeString( &(pScb->NdsTreeName),
+ &ConnectionName,
+ TRUE ) ) ) {
+
+ //
+ // No dice. Move onto the next one.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ continue;
+
+ }
+
+ //
+ // Ok, we definitely want to report this one.
+ //
+
+ Status = WriteConnStatusEntry( pScb,
+ OutputBuffer,
+ OutputBufferLength,
+ &dwBytesWritten,
+ &dwBytesNeeded,
+ (BOOLEAN)( pScb == pCallerScb ) );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // If we couldn't write this entry, then we have to update
+ // the ResumeKey and return. We don't really know how many
+ // more there are going to be so we 'suggest' to the caller
+ // a 2k buffer size.
+ //
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = SequenceNumber;
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 2048;
+ NwDereferenceScb( pNpScb );
+ goto ExitWithCleanup;
+
+ } else {
+
+ OutputBuffer = ( OutputBuffer + dwBytesWritten );
+ OutputBufferLength -= dwBytesWritten;
+ dwReturned++;
+ }
+
+ //
+ // Move to next entry in the list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ }
+
+ //
+ // We made it through the list.
+ //
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = 0;
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0;
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ //
+ // If we returned any entries, then set the status to success.
+ //
+
+ if ( dwReturned ) {
+
+ ASSERT( SequenceNumber != 0 );
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( UidServer.Buffer != NULL ) {
+ FREE_POOL( UidServer.Buffer );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetConnectionInfo(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetConnectionInfo:
+
+ Takes a connection name from the new shell and returns
+ some info commonly requested by property sheets and the
+ such.
+
+ The following connection names are supported:
+
+ Drive Letter: "X:"
+ Printer Port: "LPTX:"
+ UNC Name: "\\SERVER\Share\{Path\}
+
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer;
+ PCONN_INFORMATION pConnInfo;
+ ULONG InputBufferLength, OutputBufferLength;
+ ULONG BytesNeeded;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING ConnectionName;
+ UNICODE_STRING UidVolumeName;
+ WCHAR DriveLetter = 0;
+
+ BOOLEAN OwnRcb = FALSE;
+ BOOLEAN ReferenceVcb = FALSE;
+ PVCB Vcb = NULL;
+ PSCB Scb = NULL;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+
+ PLOGON pLogon;
+ UNICODE_STRING CredentialName;
+ UNICODE_STRING ServerName;
+ PUNICODE_STRING puUserName = NULL;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ BOOLEAN fHoldingCredentials = FALSE;
+
+ //
+ // Get the input and output buffers.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&pConnInfo );
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ RtlInitUnicodeString( &UidVolumeName, NULL );
+
+ ConnectionName.Length = (USHORT)(InputBuffer->Parameters).GetConnInfo.ConnectionNameLength;
+ ConnectionName.MaximumLength = ConnectionName.Length;
+ ConnectionName.Buffer = &((InputBuffer->Parameters).GetConnInfo.ConnectionName[0]);
+
+ //
+ // Ok, this gets a little hand-wavey, but we have to try and figure
+ // what this connection name represents.
+ //
+
+ if ( ConnectionName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = ConnectionName.Buffer[0];
+ } else if ( ConnectionName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = ConnectionName.Buffer[3];
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ if ( DriveLetter != 0 ) {
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: Drive %wZ\n", &ConnectionName );
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) ||
+ ( DriveLetter >= L'1' && DriveLetter <= L'9' ) );
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ( ( Vcb != NULL ) && ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ } else {
+
+ //
+ // This is a UNC path. Skip over the backslashes and
+ // prepend the unicode uid.
+ //
+
+ ConnectionName.Length -= (2 * sizeof( WCHAR ) );
+ ConnectionName.Buffer += 2;
+
+ Status = MakeUidServer( &UidVolumeName, &Uid, &ConnectionName );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: %wZ\n", &UidVolumeName );
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 );
+
+ if ( Prefix != NULL ) {
+ Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry );
+
+ if ( Vcb->Name.Length != UidVolumeName.Length ) {
+ Vcb = NULL;
+ }
+ }
+ }
+
+ if ( !Vcb ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: Vcb is 0x%08lx\n", Vcb );
+
+ NwReferenceVcb( Vcb );
+ ReferenceVcb = TRUE;
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ //
+ // Get the username. This is the same code block as in
+ // WriteConnStatusEntry; it should be abstracted out.
+ //
+
+ Scb = Vcb->Scb;
+ ASSERT( Scb != NULL );
+
+ if ( ( Scb->MajorVersion > 3 ) &&
+ ( Scb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &(Scb->NdsTreeName),
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ fHoldingCredentials = TRUE;
+
+ if ( pNdsContext->Credential != NULL ) {
+
+ CredentialName.Length = pNdsContext->Credential->userNameLength -
+ sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ puUserName = &CredentialName;
+ }
+
+ }
+ }
+
+ } else {
+
+ puUserName = &(Scb->UserName);
+
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: UserName %wZ\n", puUserName );
+
+ //
+ // Strip off the uid from the server name.
+ //
+
+ ServerName.Length = (Scb->UidServerName).Length;
+ ServerName.Buffer = (Scb->UidServerName).Buffer;
+
+ while ( ServerName.Length ) {
+
+ if ( ServerName.Buffer[0] == L'\\' ) {
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+ break;
+ }
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: ServerName %wZ\n", &ServerName );
+
+ //
+ // Write a single CONN_INFORMATION structure into the output buffer.
+ //
+
+ if ( puUserName ) {
+
+ BytesNeeded = sizeof( CONN_INFORMATION ) +
+ ServerName.Length +
+ puUserName->Length;
+ } else {
+
+ BytesNeeded = sizeof( CONN_INFORMATION ) +
+ ServerName.Length;
+
+ }
+
+ if ( BytesNeeded > OutputBufferLength ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ pConnInfo->HostServerLength = ServerName.Length;
+ pConnInfo->HostServer = (LPWSTR) ( (PBYTE) pConnInfo ) + sizeof( CONN_INFORMATION );
+ RtlCopyMemory( pConnInfo->HostServer, ServerName.Buffer, ServerName.Length );
+
+ pConnInfo->UserName = (LPWSTR) ( ( (PBYTE) pConnInfo->HostServer ) +
+ ServerName.Length );
+
+ if ( puUserName ) {
+
+ pConnInfo->UserNameLength = puUserName->Length;
+ RtlCopyMemory( pConnInfo->UserName, puUserName->Buffer, puUserName->Length );
+
+ } else {
+
+ pConnInfo->UserNameLength = 0;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentials ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( ReferenceVcb ) {
+ NwDereferenceVcb( Vcb, NULL, FALSE );
+ }
+
+ if ( UidVolumeName.Buffer ) {
+ FREE_POOL( UidVolumeName.Buffer );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetPreferredServer(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetPreferredServer:
+
+ Returns the current preferred server.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ BYTE *OutputBuffer;
+ ULONG OutputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ PLOGON pLogon;
+
+ PUNICODE_STRING PreferredServer;
+
+ //
+ // Get the output buffer.
+ //
+
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Get the logon structure for the user and return the preferred server.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+
+ if ( ( pLogon ) &&
+ ( pLogon->ServerName.Length ) &&
+ ( ( pLogon->ServerName.Length + sizeof( UNICODE_STRING ) ) <= OutputBufferLength ) ) {
+
+ PreferredServer = (PUNICODE_STRING) OutputBuffer;
+ PreferredServer->Length = pLogon->ServerName.Length;
+ PreferredServer->MaximumLength = pLogon->ServerName.Length;
+ PreferredServer->Buffer = ( PWCHAR ) ( OutputBuffer + sizeof( UNICODE_STRING ) );
+
+ RtlCopyMemory( PreferredServer->Buffer,
+ pLogon->ServerName.Buffer,
+ pLogon->ServerName.Length );
+
+ Status = STATUS_SUCCESS;
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ return Status;
+}
+
+NTSTATUS
+GetConnectionPerformance(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetConnectionPerformance:
+
+ Takes a connection name from the new shell and returns
+ some estimated performance info to the shell so the shell
+ can decide whether or not it wants to download icons, etc.
+
+ The following connection names are supported:
+
+ Drive Letter: "X:"
+ Printer Port: "LPTX:"
+ UNC Name: "\\SERVER\Share\{Path\}
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING RemoteName;
+
+ WCHAR DriveLetter = 0;
+ BOOLEAN OwnRcb = FALSE;
+ BOOLEAN ReferenceScb = FALSE;
+ PVCB Vcb = NULL;
+ PSCB Scb = NULL;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING OriginalUnc;
+
+ //
+ // Get the input buffer.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ //
+ // Get the UID for the caller.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ //
+ // Dig out the remote name.
+ //
+
+ RemoteName.Length = (USHORT)(InputBuffer->Parameters).GetConnPerformance.RemoteNameLength;
+ RemoteName.MaximumLength = RemoteName.Length;
+ RemoteName.Buffer = &((InputBuffer->Parameters).GetConnPerformance.RemoteName[0]);
+
+ //
+ // Ok, this gets a little hand-wavey, but we have to try and figure
+ // what this connection name represents (just like in GetConnectionInfo).
+ //
+
+ if ( RemoteName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = RemoteName.Buffer[0];
+ } else if ( RemoteName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = RemoteName.Buffer[3];
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ DebugTrace( 0, Dbg, "GetConnectionPerformance: Remote Name %wZ\n", &RemoteName );
+
+ if ( DriveLetter != 0 ) {
+
+ if ( ! ( ( ( DriveLetter >= L'a' ) && ( DriveLetter <= L'z' ) ) ||
+ ( ( DriveLetter >= L'A' ) && ( DriveLetter <= L'Z' ) ) ||
+ ( ( DriveLetter >= L'0' ) && ( DriveLetter <= L'9' ) ) ) ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ if ( DriveLetter >= L'a' && DriveLetter <= L'z' ) {
+ DriveLetter += (WCHAR) ( L'A' - L'a' );
+ }
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Did we get a connection?
+ //
+
+ if ( Vcb == NULL ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ Scb = Vcb->Scb;
+
+ } else {
+
+ //
+ // It's valid for the shell to pass us the remote name of a drive
+ // with no reference to the drive at all. Since we file these in
+ // volume prefix table with their drive letter information, we won't
+ // find them if we do a flat munge and lookup. Therefore, we have
+ // to walk the global vcb list and find the match.
+ //
+
+ //
+ // Skip over the first slash of the provided UNC remote name.
+ //
+
+ RemoteName.Length -= sizeof( WCHAR );
+ RemoteName.Buffer += 1;
+
+ for ( ListEntry = GlobalVcbList.Flink;
+ ( ListEntry != &GlobalVcbList ) && ( Scb == NULL );
+ ListEntry = ListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry );
+
+ OriginalUnc.Length = Vcb->Name.Length;
+ OriginalUnc.MaximumLength = Vcb->Name.MaximumLength;
+ OriginalUnc.Buffer = Vcb->Name.Buffer;
+
+ if ( Vcb->DriveLetter ) {
+
+ //
+ // Try it as a drive connection.
+ //
+
+ while ( ( OriginalUnc.Length ) &&
+ ( OriginalUnc.Buffer[0] != L':' ) ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+ }
+
+ if ( OriginalUnc.Buffer[0] == L':' ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+
+ if ( RtlEqualUnicodeString( &OriginalUnc,
+ &RemoteName,
+ TRUE ) ) {
+ Scb = Vcb->Scb;
+ }
+ }
+
+ } else {
+
+ //
+ // Try it as a UNC connection; start by skipping
+ // only the leading slash, the walking to the next
+ // slash.
+ //
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+
+ while ( ( OriginalUnc.Length ) &&
+ ( OriginalUnc.Buffer[0] != L'\\' ) ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+ }
+
+ if ( OriginalUnc.Length ) {
+
+ if ( RtlEqualUnicodeString( &OriginalUnc,
+ &RemoteName,
+ TRUE ) ) {
+ Scb = Vcb->Scb;
+ }
+ }
+
+ }
+ }
+
+ }
+
+ if ( !Scb ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ NwReferenceScb( Scb->pNpScb );
+ ReferenceScb = TRUE;
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ DebugTrace( 0, Dbg, "GetConnectionPerformance: Scb is 0x%08lx\n", Scb );
+
+ //
+ // Now dig out the performance info from the LIP negotiation.
+ //
+ // dwSpeed - The speed of the media to the network resource in units of 100bps (e.g 1,200
+ // baud point to point link returns 12).
+ // dwDelay - The delay introduced by the network when sending information (i.e. the time
+ // between starting sending data and the time that it starts being received) in
+ // units of a millisecond. This is in addition to any latency that was incorporated
+ // into the calculation of dwSpeed, so the value returned will be 0 for accessing
+ // most resources.
+ // dwOptDataSize - A recommendation for the size of data in bytes that is most efficiently
+ // sent through the network when an application makes a single request to
+ // the network resource. For example, for a disk network resource, this
+ // value might be 2048 or 512 when writing a block of data.
+
+ (InputBuffer->Parameters).GetConnPerformance.dwFlags = WNCON_DYNAMIC;
+ (InputBuffer->Parameters).GetConnPerformance.dwDelay = 0;
+ (InputBuffer->Parameters).GetConnPerformance.dwOptDataSize = Scb->pNpScb->BufferSize;
+ (InputBuffer->Parameters).GetConnPerformance.dwSpeed = Scb->pNpScb->LipDataSpeed;
+
+ //
+ // BUGBUG: We don't return any good speed info for servers that have not yet
+ // negotiated lip. We may return out of date information for servers that have
+ // become disconnected unless a RAS line transition occurred. This API is bogus.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( ReferenceScb ) {
+ NwDereferenceScb( Scb->pNpScb );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+SetShareBit(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ )
+/*+++
+
+SetShareBit:
+
+ This function sets the share bit on a file.
+ The bit won't get set until all handles to the
+ file are closed.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB pIcb;
+ PFCB pFcb;
+ PVOID fsContext, fsContext2;
+
+ DebugTrace( 0, Dbg, "SetShareBit.\n", 0 );
+
+ //
+ // Make sure this is a handle to a file.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 );
+
+ if ( nodeTypeCode != NW_NTC_ICB ) {
+ DebugTrace( 0, Dbg, "You can only set the share bit on a file!\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pIcb = (PICB) fsContext2;
+ pFcb = pIcb->SuperType.Fcb;
+
+ if ( pFcb->NodeTypeCode != NW_NTC_FCB ) {
+ DebugTrace( 0, Dbg, "You can't set the share bit on a directory!\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Acquire this FCB so we can muck with the flags.
+ //
+
+ NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE );
+
+ SetFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE );
+
+ NwReleaseFcb( pFcb->NonPagedFcb );
+
+ return STATUS_SUCCESS;
+
+}
+
+VOID
+LazySetShareable(
+ PIRP_CONTEXT IrpContext,
+ PICB pIcb,
+ PFCB pFcb
+)
+/***
+
+Function Description:
+
+ This function gets called everytime an ICB with a remote handle
+ is closed. If we are closing the last ICB to an FCB and the
+ caller has requested that we set the shareable bit on the FCB,
+ then we need to do so now. Otherwise, we simply return.
+
+Caveats:
+
+ If we fail to set the shareable bit, there is no way to notify
+ the requestor of the operation that the operation was not carried
+ out.
+
+***/
+{
+
+ NTSTATUS Status;
+
+ PLIST_ENTRY IcbListEntry;
+ PICB pCurrentIcb;
+ BOOLEAN OtherHandlesExist = FALSE;
+
+ ULONG Attributes;
+ BOOLEAN AttributesAreValid = FALSE;
+
+
+ //
+ // Get to the head of the queue, acquire the RCB,
+ // and acquire this FCB to protect the ICB list
+ // and FCB flags.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE );
+
+ //
+ // Scan the other ICBs on this FCB to see if any of
+ // them have remote handles.
+ //
+
+ for ( IcbListEntry = pFcb->IcbList.Flink;
+ IcbListEntry != &(pFcb->IcbList) ;
+ IcbListEntry = IcbListEntry->Flink ) {
+
+ pCurrentIcb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry );
+
+ if ( ( pCurrentIcb != pIcb ) &&
+ ( pCurrentIcb->HasRemoteHandle ) ) {
+ OtherHandlesExist = TRUE;
+ }
+ }
+
+ if ( OtherHandlesExist ) {
+
+ //
+ // We'll do it when the last handle is closed.
+ //
+
+ DebugTrace( 0, Dbg, "LazySetShareable: This isn't the last remote handle.\n", 0 );
+ goto ReleaseAllAndExit;
+ }
+
+ //
+ // We're closing the last handle. Make sure we have valid attributes.
+ //
+
+ if ( !FlagOn( pFcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+
+ if ( !BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ pFcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &pFcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse( IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+
+ if ( NT_SUCCESS( Status ) ) {
+ AttributesAreValid = TRUE;
+ }
+ }
+
+ } else {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_INFO_ATTRIBUTES,
+ pFcb->Vcb->Specific.Disk.VolumeNumber,
+ pFcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &pFcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse( IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e",
+ 4,
+ &Attributes );
+
+ if ( NT_SUCCESS( Status ) ) {
+ AttributesAreValid = TRUE;
+ }
+
+ }
+
+ }
+
+ } else {
+
+ Attributes = pFcb->NonPagedFcb->Attributes;
+ AttributesAreValid = TRUE;
+ }
+
+ if ( !AttributesAreValid ) {
+ DebugTrace( 0, Dbg, "Couldn't get valid attributes for this file.\n", 0 );
+ goto ReleaseAllAndExit;
+ }
+
+ //
+ // Do the set with the shareable bit on!
+ //
+
+ if ( BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait( IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDW--WW==WW==_W_bDbC",
+ NCP_LFN_SET_INFO,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_SET_INFO_ATTRIBUTES,
+ Attributes | 0x80,
+ 0,
+ 0,
+ 0,
+ 0,
+ 8,
+ 0,
+ 8,
+ pFcb->Vcb->Specific.Disk.VolumeNumber,
+ pFcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &pFcb->RelativeFileName );
+
+ } else {
+
+ Status = ExchangeWithWait( IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ Attributes | 0x80,
+ pFcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &pFcb->RelativeFileName );
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Failed to set the shareable attribute on the file.\n", 0 );
+ ASSERT( FALSE && "File NOT marked as shareable!!" );
+ } else {
+ DebugTrace( 0, Dbg, "Shareable bit successfully set.\n", 0 );
+ ClearFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE );
+ }
+
+ReleaseAllAndExit:
+
+ NwReleaseFcb( pFcb->NonPagedFcb );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+ return;
+}
diff --git a/private/nw/rdr/fspdisp.c b/private/nw/rdr/fspdisp.c
new file mode 100644
index 000000000..bd21d7d2d
--- /dev/null
+++ b/private/nw/rdr/fspdisp.c
@@ -0,0 +1,232 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ FspDisp.c
+
+Abstract:
+
+ This module implements the main dispatch procedure/thread for the NetWare
+ Fsp
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// Define our local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFspDispatch )
+#endif
+
+#if 0 // Not pageable
+NwPostToFsp
+#endif
+
+
+VOID
+NwFspDispatch (
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main FSP thread routine that is executed to receive
+ and dispatch IRP requests. Each FSP thread begins its execution here.
+ There is one thread created at system initialization time and subsequent
+ threads created as needed.
+
+Arguments:
+
+
+ Context - Supplies the thread id.
+
+Return Value:
+
+ None - This routine never exits
+
+--*/
+
+{
+ PIRP Irp;
+ PIRP_CONTEXT IrpContext;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ PPOST_PROCESSOR PostProcessRoutine;
+ BOOLEAN TopLevel;
+
+ IrpContext = (PIRP_CONTEXT)Context;
+
+ Irp = IrpContext->pOriginalIrp;
+ ClearFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+
+ //
+ // Now case on the function code. For each major function code,
+ // either call the appropriate FSP routine or case on the minor
+ // function and then call the FSP routine. The FSP routine that
+ // we call is responsible for completing the IRP, and not us.
+ // That way the routine can complete the IRP and then continue
+ // post processing as required. For example, a read can be
+ // satisfied right away and then read can be done.
+ //
+ // We'll do all of the work within an exception handler that
+ // will be invoked if ever some underlying operation gets into
+ // trouble (e.g., if NwReadSectorsSync has trouble).
+ //
+
+
+ DebugTrace(0, Dbg, "NwFspDispatch: Irp = 0x%08lx\n", Irp);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ //
+ // If we have a run routine for this IRP context, then run it,
+ // if not fall through to the IRP handler.
+ //
+
+ if ( IrpContext->PostProcessRoutine != NULL ) {
+
+ PostProcessRoutine = IrpContext->PostProcessRoutine;
+
+ //
+ // Clear the run routine so that we don't run it again.
+ //
+
+ IrpContext->PostProcessRoutine = NULL;
+
+ Status = PostProcessRoutine( IrpContext );
+
+ } else {
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ switch ( IrpSp->MajorFunction ) {
+
+ //
+ // For File System Control operations,
+ //
+
+ case IRP_MJ_FILE_SYSTEM_CONTROL:
+
+ Status = NwCommonFileSystemControl( IrpContext );
+ break;
+
+ //
+ // For any other major operations, return an invalid
+ // request.
+ //
+
+ default:
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ }
+
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest( IrpContext, Status );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ (VOID) NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ return;
+}
+
+
+NTSTATUS
+NwPostToFsp (
+ IN PIRP_CONTEXT IrpContext,
+ IN BOOLEAN MarkIrpPending
+ )
+
+/*++
+
+Routine Description:
+
+ This routine post an IRP context to an executive worker thread
+ for FSP level processing.
+
+ *** WARNING: After calling this routine, the caller may no
+ longer access IrpContext. This routine passes
+ the IrpContext to the FSP which may run and free
+ the IrpContext before this routine returns to the
+ caller.
+
+Arguments:
+
+ IrpContext - Supplies the Irp being processed.
+
+ MarkIrpPending - If true, mark the IRP pending.
+
+Return Value:
+
+ STATUS_PENDING.
+
+--*/
+
+{
+ PIRP Irp = IrpContext->pOriginalIrp;
+
+ DebugTrace(0, Dbg, "NwPostToFsp: IrpContext = %X\n", IrpContext );
+ DebugTrace(0, Dbg, "PostProcessRoutine = %X\n", IrpContext->PostProcessRoutine );
+
+ if ( MarkIrpPending ) {
+
+ //
+ // Mark this I/O request as being pending.
+ //
+
+ IoMarkIrpPending( Irp );
+ }
+
+ //
+ // Queue to IRP context to an ex worker thread.
+ //
+
+ ExInitializeWorkItem( &IrpContext->WorkQueueItem, NwFspDispatch, IrpContext );
+ ExQueueWorkItem( &IrpContext->WorkQueueItem, DelayedWorkQueue );
+
+ return( STATUS_PENDING );
+}
+
diff --git a/private/nw/rdr/init.c b/private/nw/rdr/init.c
new file mode 100644
index 000000000..e24479ab7
--- /dev/null
+++ b/private/nw/rdr/init.c
@@ -0,0 +1,460 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ NwInit.c
+
+Abstract:
+
+ This module implements the DRIVER_INITIALIZATION routine for NetWare
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#define Dbg (DEBUG_TRACE_LOAD)
+
+//
+// Private declaration because ZwQueryDefaultLocale isn't in any header.
+//
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwQueryDefaultLocale(
+ IN BOOLEAN UserProfile,
+ OUT PLCID DefaultLocaleId
+ );
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+VOID
+UnloadDriver(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+VOID
+GetConfigurationInformation(
+ PUNICODE_STRING RegistryPath
+ );
+
+VOID
+ReadValue(
+ HANDLE ParametersHandle,
+ PLONG pVar,
+ PWCHAR Name
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, DriverEntry )
+#pragma alloc_text( PAGE, GetConfigurationInformation )
+#pragma alloc_text( PAGE, ReadValue )
+#endif
+
+#if 0 // Not pageable
+UnloadDriver
+#endif
+
+static ULONG IrpStackSize;
+
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ This is the initialization routine for the Nw file system
+ device driver. This routine creates the device object for the FileSystem
+ device and performs all other driver initialization.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by the system.
+
+Return Value:
+
+ NTSTATUS - The function value is the final status from the initialization
+ operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING UnicodeString;
+ PAGED_CODE();
+
+ //DbgBreakPoint();
+
+ InitializeAttach( );
+ NwInitializeData();
+ NwInitializePidTable();
+
+ //
+ // Create the device object.
+ //
+
+ RtlInitUnicodeString( &UnicodeString, DD_NWFS_DEVICE_NAME_U );
+ Status = IoCreateDevice( DriverObject,
+ 0,
+ &UnicodeString,
+ FILE_DEVICE_NETWORK_FILE_SYSTEM,
+ FILE_REMOTE_DEVICE,
+ FALSE,
+ &FileSystemDeviceObject );
+
+ if (!NT_SUCCESS( Status )) {
+ Error(EVENT_NWRDR_CANT_CREATE_DEVICE, Status, NULL, 0, 0);
+ return Status;
+ }
+
+ //
+ // Initialize parameters to the defaults.
+ //
+
+ IrpStackSize = NWRDR_IO_STACKSIZE;
+
+ //
+ // Attempt to read config information from the registry
+ //
+
+ GetConfigurationInformation( RegistryPath );
+
+ //
+ // Set the stack size.
+ //
+
+ FileSystemDeviceObject->StackSize = (CCHAR)IrpStackSize;
+
+ //
+ // Initialize the driver object with this driver's entry points.
+ //
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)NwFsdCreate;
+ DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)NwFsdCleanup;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)NwFsdClose;
+ DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)NwFsdFileSystemControl;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)NwFsdDeviceIoControl;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)NwFsdQueryInformation;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)NwFsdQueryVolumeInformation;
+ DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)NwFsdSetVolumeInformation;
+ DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)NwFsdDirectoryControl;
+ DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)NwFsdRead;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)NwFsdWrite;
+ DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)NwFsdSetInformation;
+ DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)NwFsdLockControl;
+ DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)NwFsdFlushBuffers;
+/*
+ DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)NwFsdQueryEa;
+ DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)NwFsdSetEa;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)NwFsdShutdown;
+*/
+ DriverObject->DriverUnload = UnloadDriver;
+
+#if NWFASTIO
+ DriverObject->FastIoDispatch = &NwFastIoDispatch;
+
+ NwFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
+ NwFastIoDispatch.FastIoCheckIfPossible = NULL;
+ NwFastIoDispatch.FastIoRead = NwFastRead;
+ NwFastIoDispatch.FastIoWrite = NwFastWrite;
+ NwFastIoDispatch.FastIoQueryBasicInfo = NwFastQueryBasicInfo;
+ NwFastIoDispatch.FastIoQueryStandardInfo = NwFastQueryStandardInfo;
+ NwFastIoDispatch.FastIoLock = NULL;
+ NwFastIoDispatch.FastIoUnlockSingle = NULL;
+ NwFastIoDispatch.FastIoUnlockAll = NULL;
+ NwFastIoDispatch.FastIoUnlockAllByKey = NULL;
+ NwFastIoDispatch.FastIoDeviceControl = NULL;
+#endif
+
+ NwInitializeRcb( &NwRcb );
+
+ InitializeIrpContext( );
+
+ NwPermanentNpScb.State = SCB_STATE_DISCONNECTING;
+
+ //
+ // Do a kludge here so that we get to the "real" global variables.
+ //
+
+ //NlsLeadByteInfo = *(PUSHORT *)NlsLeadByteInfo;
+ //NlsMbCodePageTag = *(*(PBOOLEAN *)&NlsMbCodePageTag);
+
+#ifndef IFS
+ FsRtlLegalAnsiCharacterArray = *(PUCHAR *)FsRtlLegalAnsiCharacterArray;
+#endif
+
+ DebugTrace(0, Dbg, "NetWare redirector loaded\n", 0);
+
+ //
+ // And return to our caller
+ //
+
+ return( STATUS_SUCCESS );
+}
+
+VOID
+UnloadDriver(
+ IN PDRIVER_OBJECT DriverObject
+ )
+/*++
+
+Routine Description:
+
+ This is the unload routine for the NetWare redirector filesystem.
+
+Arguments:
+
+ DriverObject - pointer to the driver object for the redirector
+
+Return Value:
+
+ None
+
+--*/
+{
+ KIRQL OldIrql;
+
+ PAGED_CODE();
+
+ IpxClose();
+
+ IPX_Close_Socket( &NwPermanentNpScb.Server );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ RemoveEntryList( &NwPermanentNpScb.ScbLinks );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DestroyAllScb();
+
+ UninitializeIrpContext();
+
+ if (IpxTransportName.Buffer != NULL) {
+
+ FREE_POOL(IpxTransportName.Buffer);
+
+ }
+
+ if ( NwProviderName.Buffer != NULL ) {
+ FREE_POOL( NwProviderName.Buffer );
+ }
+
+ NwUninitializePidTable();
+
+ ASSERT( IsListEmpty( &NwPagedPoolList ) );
+ ASSERT( IsListEmpty( &NwNonpagedPoolList ) );
+
+ ASSERT( MdlCount == 0 );
+ ASSERT( IrpCount == 0 );
+
+ NwDeleteRcb( &NwRcb );
+
+#ifdef NWDBG
+ ExDeleteResource( &NwDebugResource );
+#endif
+
+ ExDeleteResource( &NwOpenResource );
+ ExDeleteResource( &NwUnlockableCodeResource );
+
+ IoDeleteDevice(FileSystemDeviceObject);
+
+ DebugTrace(0, Dbg, "NetWare redirector unloaded\n\n", 0);
+
+}
+
+
+
+VOID
+GetConfigurationInformation(
+ PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ This routine read redirector configuration information from the registry.
+
+Arguments:
+
+ RegistryPath - A pointer the a path to the
+
+Return Value:
+
+ None
+
+--*/
+{
+ UNICODE_STRING UnicodeString;
+ HANDLE ConfigHandle;
+ HANDLE ParametersHandle;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG TimeOutEventinMins = 0L;
+ LCID lcid;
+
+ PAGED_CODE();
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ RegistryPath, // name
+ OBJ_CASE_INSENSITIVE, // attributes
+ NULL, // root
+ NULL // security descriptor
+ );
+
+ Status = ZwOpenKey ( &ConfigHandle, KEY_READ, &ObjectAttributes );
+
+ if (!NT_SUCCESS(Status)) {
+ return;
+ }
+
+ RtlInitUnicodeString( &UnicodeString, L"Parameters" );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeString,
+ OBJ_CASE_INSENSITIVE,
+ ConfigHandle,
+ NULL
+ );
+
+ Status = ZwOpenKey( &ParametersHandle, KEY_READ, &ObjectAttributes );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ ZwClose( ConfigHandle );
+ return;
+ }
+
+ ReadValue( ParametersHandle, &IrpStackSize, L"IrpStackSize" );
+
+ ReadValue( ParametersHandle, &MaxSendDelay, L"MaxSendDelay" );
+ ReadValue( ParametersHandle, &MaxReceiveDelay, L"MaxReceiveDelay" );
+
+ ReadValue( ParametersHandle, &MinSendDelay, L"MinSendDelay" );
+ ReadValue( ParametersHandle, &MinReceiveDelay, L"MinReceiveDelay" );
+
+ ReadValue( ParametersHandle, &BurstSuccessCount, L"BurstSuccessCount" );
+ ReadValue( ParametersHandle, &BurstSuccessCount2, L"BurstSuccessCount2" );
+ ReadValue( ParametersHandle, &MaxReadTimeout, L"MaxReadTimeout" );
+ ReadValue( ParametersHandle, &MaxWriteTimeout, L"MaxWriteTimeout" );
+ ReadValue( ParametersHandle, &ReadTimeoutMultiplier, L"ReadTimeoutMultiplier" );
+ ReadValue( ParametersHandle, &WriteTimeoutMultiplier, L"WriteTimeoutMultiplier" );
+ ReadValue( ParametersHandle, &AllowGrowth, L"AllowGrowth" );
+ ReadValue( ParametersHandle, &DontShrink, L"DontShrink" );
+ ReadValue( ParametersHandle, &SendExtraNcp, L"SendExtraNcp" );
+ ReadValue( ParametersHandle, &DefaultMaxPacketSize, L"DefaultMaxPacketSize" );
+ ReadValue( ParametersHandle, &PacketThreshold, L"PacketThreshold" );
+ ReadValue( ParametersHandle, &LargePacketAdjustment, L"LargePacketAdjustment" );
+ ReadValue( ParametersHandle, &LipPacketAdjustment, L"LipPacketAdjustment" );
+ ReadValue( ParametersHandle, &LipAccuracy, L"LipAccuracy" );
+
+ ReadValue( ParametersHandle, &DisableReadCache, L"DisableReadCache" );
+ ReadValue( ParametersHandle, &DisableWriteCache, L"DisableWriteCache" );
+ ReadValue( ParametersHandle, &FavourLongNames, L"FavourLongNames" );
+
+ ReadValue( ParametersHandle, &LockTimeoutThreshold, L"LockTimeout" );
+
+ ReadValue( ParametersHandle, &TimeOutEventinMins, L"TimeOutEventinMins");
+
+ ReadValue( ParametersHandle, &EnableMultipleConnects, L"EnableMultipleConnects");
+
+ ReadValue( ParametersHandle, &ReadExecOnlyFiles, L"ReadExecOnlyFiles");
+
+ if (!TimeOutEventinMins) {
+ //
+ // If for some reason, the registry has set the TimeOutEventInterval
+ // to zero, reset to the default value to avoid divide-by-zero
+ //
+
+ TimeOutEventinMins = DEFAULT_TIMEOUT_EVENT_INTERVAL;
+ }
+
+ TimeOutEventInterval.QuadPart = TimeOutEventinMins * 60 * SECONDS;
+
+ ZwClose( ParametersHandle );
+ ZwClose( ConfigHandle );
+
+ Japan = FALSE;
+
+
+ ZwQueryDefaultLocale( TRUE, &lcid );
+
+ if (PRIMARYLANGID(lcid) == LANG_JAPANESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN ||
+ PRIMARYLANGID(lcid) == LANG_CHINESE) {
+
+ Japan = TRUE;
+ }
+
+}
+
+
+VOID
+ReadValue(
+ HANDLE ParametersHandle,
+ PLONG pVar,
+ PWCHAR Name
+ )
+/*++
+
+Routine Description:
+
+ This routine reads a single redirector configuration value from the registry.
+
+Arguments:
+
+ Parameters - Supplies where to look for values.
+
+ pVar - Address of the variable to receive the new value if the name exists.
+
+ Name - Name whose value is to be loaded.
+
+Return Value:
+
+ None
+
+--*/
+{
+ WCHAR Storage[256];
+ UNICODE_STRING UnicodeString;
+ NTSTATUS Status;
+ ULONG BytesRead;
+ PKEY_VALUE_FULL_INFORMATION Value = (PKEY_VALUE_FULL_INFORMATION)Storage;
+
+ PAGED_CODE();
+
+ UnicodeString.Buffer = Storage;
+
+ RtlInitUnicodeString(&UnicodeString, Name );
+
+ Status = ZwQueryValueKey(
+ ParametersHandle,
+ &UnicodeString,
+ KeyValueFullInformation,
+ Value,
+ sizeof(Storage),
+ &BytesRead );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if ( Value->DataLength >= sizeof(ULONG) ) {
+
+ *pVar = *(LONG UNALIGNED *)( (PCHAR)Value + Value->DataOffset );
+
+ }
+ }
+}
diff --git a/private/nw/rdr/ipx.c b/private/nw/rdr/ipx.c
new file mode 100644
index 000000000..2341d1cd3
--- /dev/null
+++ b/private/nw/rdr/ipx.c
@@ -0,0 +1,1749 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Ipx.c
+
+Abstract:
+
+ This module implements the low level Ipx support routines for the NetWare
+ redirector.
+
+Author:
+
+ Colin Watson [ColinW] 28-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include "wsnwlink.h"
+
+//
+// Define IPX interfaces that should be in a public header file but aren't
+// (at least for NT 1.0). For Daytona, include isnkrnl.h.
+//
+
+#define IPX_ID 'M'<<24 | 'I'<<16 | 'P'<<8 | 'X'
+
+#define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8))
+#define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/
+#define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */
+#define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */
+#define MIPX_LINECHANGE I_MIPX | 310 /* queued until WAN line goes up/down */
+
+#define Dbg (DEBUG_TRACE_IPX)
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+extern POBJECT_TYPE *IoFileObjectType;
+
+typedef TA_IPX_ADDRESS UNALIGNED *PUTA_IPX_ADDRESS;
+
+typedef struct _ADDRESS_INFORMATION {
+ ULONG ActivityCount;
+ TA_IPX_ADDRESS NetworkName;
+ ULONG Unused; // Junk needed to work around streams NWLINK bug.
+} ADDRESS_INFORMATION, *PADDRESS_INFORMATION;
+
+//
+// Handle difference between NT1.0 and use of ntifs.h
+//
+#ifdef IFS
+ #define ATTACHPROCESS(_X) KeAttachProcess(_X);
+#else
+ #define ATTACHPROCESS(_X) KeAttachProcess(&(_X)->Pcb);
+#endif
+
+NTSTATUS
+SubmitTdiRequest (
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ );
+
+NTSTATUS
+CompletionEvent(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+QueryAddressInformation(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PADDRESS_INFORMATION AddressInformation
+ );
+
+NTSTATUS
+QueryProviderInformation(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PTDI_PROVIDER_INFO ProviderInfo
+ );
+
+USHORT
+GetSocketNumber(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc
+ );
+
+NTSTATUS
+SetTransportOption(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG Option
+ );
+
+#ifndef QFE_BUILD
+
+NTSTATUS
+CompletionLineChange(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+#endif
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, IPX_Get_Local_Target )
+#pragma alloc_text( PAGE, IPX_Get_Internetwork_Address )
+#pragma alloc_text( PAGE, IPX_Get_Interval_Marker )
+#pragma alloc_text( PAGE, IPX_Open_Socket )
+#pragma alloc_text( PAGE, IPX_Close_Socket )
+#pragma alloc_text( PAGE, IpxOpen )
+#pragma alloc_text( PAGE, IpxOpenHandle )
+#pragma alloc_text( PAGE, BuildIpxAddressEa )
+#pragma alloc_text( PAGE, IpxClose )
+#pragma alloc_text( PAGE, SetEventHandler )
+#pragma alloc_text( PAGE, SubmitTdiRequest )
+#pragma alloc_text( PAGE, GetSocketNumber )
+#pragma alloc_text( PAGE, GetMaximumPacketSize )
+#pragma alloc_text( PAGE, QueryAddressInformation )
+#pragma alloc_text( PAGE, QueryProviderInformation )
+#pragma alloc_text( PAGE, SetTransportOption )
+#pragma alloc_text( PAGE, GetNewRoute )
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE, SubmitLineChangeRequest )
+#pragma alloc_text( PAGE, FspProcessLineChange )
+#endif
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, CompletionEvent )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+BuildIpxAddress
+CompletionLineChange
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+IPX_Get_Local_Target(
+ IN IPXaddress* RemoteAddress,
+ OUT NodeAddress* LocalTarget,
+ OUT word* Ticks
+ )
+/*++
+
+Routine Description:
+
+ Determine the address in the caller's own network to which to transmit
+ in order to reach the specified machine.
+
+ This is not required for NT since the IPX transport handles the
+ issue of determining routing between this machine and the remote
+ address.
+
+Arguments:
+
+ RemoteAddress - Supplies the remote computers address
+ NodeAddress - Where to store the intermediate machine address
+ Ticks - Returns the expected number of ticks to reach the remote address
+
+Return Value:
+
+ status of the operation
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "IPX_Get_Local_Target\n", 0);
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+VOID
+IPX_Get_Internetwork_Address(
+ OUT IPXaddress* LocalAddress
+ )
+/*++
+
+Routine Description:
+
+ Determine the callers full address in a set of interconnected networks.
+ in order to reach the specified machine.
+
+ This is not required for NT since the IPX transport handles the
+ issue of determining routing between this machine and the remote
+ address.
+
+Arguments:
+
+ LocalAddress - Where to store the local address
+
+Return Value:
+
+ none
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "IPX_Get_Internetwork_Address\n", 0);
+ RtlFillMemory(LocalAddress, sizeof(IPXaddress), 0xff);
+}
+
+
+word
+IPX_Get_Interval_Marker(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Determine the interval marker in clock ticks.
+
+Arguments:
+
+Return Value:
+
+ interval marker
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "IPX_Get_Interval_Marker\n", 0);
+ return 0xff;
+}
+
+
+NTSTATUS
+IPX_Open_Socket(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc
+ )
+/*++
+
+Routine Description:
+
+ Open a local socket to be used for a conection to a remote server.
+
+Arguments:
+
+ pIrpC - supplies the irp context for the request creating the socket.
+
+ pTdiStruc - supplies where to record the handle and both device and file
+ object pointers
+
+Return Value:
+
+ 0 success
+
+--*/
+{
+ NTSTATUS Status;
+ UCHAR NetworkName[ sizeof( FILE_FULL_EA_INFORMATION )-1 +
+ TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
+ sizeof(TA_IPX_ADDRESS)];
+
+ static UCHAR LocalNodeAddress[6] = {0,0,0,0,0,0};
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IPX_Open_Socket %X\n", pTdiStruc->Socket);
+
+ //
+ // Let the transport decide the network number and node address
+ // if the caller specified socket 0. This will allow the transport
+ // to use whatever local adapters are available to get to the
+ // remote server.
+ //
+
+ BuildIpxAddressEa( (ULONG)0,
+ LocalNodeAddress,
+ (USHORT)pTdiStruc->Socket,
+ &NetworkName );
+
+ Status = IpxOpenHandle( &pTdiStruc->Handle,
+ &pTdiStruc->pDeviceObject,
+ &pTdiStruc->pFileObject,
+ &NetworkName,
+ FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
+ TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
+ sizeof(TA_IPX_ADDRESS));
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ if ( pTdiStruc->Socket == 0 ) {
+
+ //
+ // Find out the socket number assigned by the transport
+ //
+
+ pTdiStruc->Socket = GetSocketNumber( pIrpC, pTdiStruc );
+ DebugTrace(0, Dbg, "Assigned socket number %X\n", pTdiStruc->Socket );
+ }
+
+ //
+ // Tell transport to accept packet type being set in the connection
+ // information provided with the send datagram. Transport reports
+ // the packet type similarly on receive datagram.
+ // BUGBUG we should get the ioctl codes for ipx into a standard place
+ //
+
+ Status = SetTransportOption(
+ pIrpC,
+ pTdiStruc,
+ MIPX_SENDPTYPE );
+
+ DebugTrace(-1, Dbg, " %X\n", Status );
+ return Status;
+}
+
+
+
+VOID
+IPX_Close_Socket(
+ IN PNW_TDI_STRUCT pTdiStruc
+ )
+/*++
+
+Routine Description:
+
+ Terminate a connection over the network.
+
+Arguments:
+
+ pTdiStruc - supplies where to record the handle and both device and file
+ object pointers
+
+Return Value:
+
+ none
+
+--*/
+{
+ BOOLEAN ProcessAttached = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IPX_Close_Socket %x\n", pTdiStruc->Socket);
+
+ if ( pTdiStruc->Handle == NULL ) {
+ return;
+ }
+
+ ObDereferenceObject( pTdiStruc->pFileObject );
+
+ //
+ // Attach to the redirector's FSP to allow the handle for the
+ // connection to hang around.
+ //
+
+ if (PsGetCurrentProcess() != FspProcess) {
+ ATTACHPROCESS(FspProcess);
+ ProcessAttached = TRUE;
+ }
+
+ ZwClose( pTdiStruc->Handle );
+
+ if (ProcessAttached) {
+ //
+ // Now re-attach back to our original process
+ //
+
+ KeDetachProcess();
+ }
+
+ pTdiStruc->Handle = NULL;
+
+ pTdiStruc->pFileObject = NULL;
+
+ DebugTrace(-1, Dbg, "IPX_Close_Socket\n", 0);
+ return;
+}
+
+
+NTSTATUS
+IpxOpen(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Open handle to the Ipx transport.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none
+
+--*/
+{
+ NTSTATUS Status;
+
+ Status = IpxOpenHandle( &IpxHandle,
+ &pIpxDeviceObject,
+ &pIpxFileObject,
+ NULL,
+ 0 );
+
+ DebugTrace(-1, Dbg, "IpxOpen of local node address %X\n", Status);
+ return Status;
+}
+
+
+NTSTATUS
+IpxOpenHandle(
+ OUT PHANDLE pHandle,
+ OUT PDEVICE_OBJECT* ppDeviceObject,
+ OUT PFILE_OBJECT* ppFileObject,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength
+ )
+/*++
+
+Routine Description:
+
+ Open handle to the Ipx transport.
+
+Arguments:
+
+ OUT Handle - The handle to the transport if return value is NT_SUCCESS
+
+Return Value:
+
+ none
+
+--*/
+{
+ OBJECT_ATTRIBUTES AddressAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS Status;
+ BOOLEAN ProcessAttached = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IpxOpenHandle\n", 0);
+
+ *pHandle = NULL;
+
+ InitializeObjectAttributes (&AddressAttributes,
+ &IpxTransportName,
+ OBJ_CASE_INSENSITIVE,// Attributes
+ NULL, // RootDirectory
+ NULL); // SecurityDescriptor
+
+ //
+ // Attach to the redirector's FSP to allow the handle for the
+ // connection to hang around. Normally we create 3 handles at once
+ // so the outer code already has done this to avoid the expensive
+ // attach procedure.
+ //
+
+ if (PsGetCurrentProcess() != FspProcess) {
+ ATTACHPROCESS(FspProcess);
+ ProcessAttached = TRUE;
+ }
+
+ Status = ZwCreateFile(pHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &AddressAttributes, // Object Attributes
+ &IoStatusBlock, // Final I/O status block
+ NULL, // Allocation Size
+ FILE_ATTRIBUTE_NORMAL, // Normal attributes
+ FILE_SHARE_READ,// Sharing attributes
+ FILE_OPEN_IF, // Create disposition
+ 0, // CreateOptions
+ EaBuffer,EaLength);
+
+ if (!NT_SUCCESS(Status) ||
+ !NT_SUCCESS(Status = IoStatusBlock.Status)) {
+
+ goto error_cleanup2;
+
+ }
+
+ //
+ // Obtain a referenced pointer to the file object.
+ //
+ Status = ObReferenceObjectByHandle (
+ *pHandle,
+ 0,
+ NULL,
+ KernelMode,
+ ppFileObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto error_cleanup;
+
+ }
+
+ if (ProcessAttached) {
+
+ //
+ // Now re-attach back to our original process
+ //
+
+ KeDetachProcess();
+ }
+
+ *ppDeviceObject = IoGetRelatedDeviceObject( *ppFileObject );
+
+ DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
+ return Status;
+
+error_cleanup2:
+
+ ASSERT( *pHandle != NULL );
+ ZwClose( *pHandle );
+ *pHandle = NULL;
+
+error_cleanup:
+ if (ProcessAttached) {
+
+ //
+ // Now re-attach back to our original process
+ //
+
+ KeDetachProcess();
+ }
+
+ DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
+ return Status;
+}
+
+
+VOID
+BuildIpxAddress(
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PTA_IPX_ADDRESS NetworkName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a TA_NETBIOS_ADDRESS structure in the locations pointed
+ to by NetworkName. All fields are filled out.
+
+Arguments:
+ NetworkAddress - Supplies the network number
+ NodeAddress - Supplies the node number
+ Socket - The socket number (in Hi-Lo order)
+ NetworkName - Supplies the structure to place the address
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ // Warn compiler that TAAddressCount may be mis-aligned.
+ PUTA_IPX_ADDRESS UNetworkName = (PUTA_IPX_ADDRESS)NetworkName;
+
+ DebugTrace(+0, Dbg, "BuildIpxAddress\n", 0);
+
+ UNetworkName->TAAddressCount = 1;
+ UNetworkName->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX;
+ UNetworkName->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IPX;
+
+ RtlMoveMemory (
+ UNetworkName->Address[0].Address[0].NodeAddress,
+ NodeAddress,
+ 6);
+ UNetworkName->Address[0].Address[0].NetworkAddress = NetworkAddress;
+ UNetworkName->Address[0].Address[0].Socket = Socket;
+
+} /* TdiBuildIpxAddress */
+
+
+VOID
+BuildIpxAddressEa (
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PVOID NetworkName
+ )
+
+/*++
+
+Routine Description:
+
+ Builds an EA describing a Netbios address in the buffer supplied by the
+ user.
+
+Arguments:
+
+ NetworkAddress - Supplies the network number
+ NodeAddress - Supplies the node number
+ Socket -
+ NetworkName - The Ea structure that describes the input parameters.
+
+Return Value:
+
+ An informative error code if something goes wrong. STATUS_SUCCESS if the
+ ea is built properly.
+
+--*/
+
+{
+ PFILE_FULL_EA_INFORMATION EaBuffer;
+ PTA_IPX_ADDRESS TAAddress;
+ ULONG Length;
+
+ DebugTrace(+0, Dbg, "BuildIpxAddressEa\n", 0);
+
+ Length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
+ TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
+ sizeof (TA_IPX_ADDRESS);
+ EaBuffer = (PFILE_FULL_EA_INFORMATION)NetworkName;
+
+ EaBuffer->NextEntryOffset = 0;
+ EaBuffer->Flags = 0;
+ EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
+ EaBuffer->EaValueLength = sizeof (TA_IPX_ADDRESS);
+
+ RtlCopyMemory (
+ EaBuffer->EaName,
+ TdiTransportAddress,
+ EaBuffer->EaNameLength + 1);
+
+ TAAddress = (PTA_IPX_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1];
+
+ BuildIpxAddress(
+ NetworkAddress,
+ NodeAddress,
+ Socket,
+ TAAddress);
+
+
+ return;
+
+}
+
+
+VOID
+IpxClose(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Open handle to the Ipx transport.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IpxClose...\n", 0);
+ if ( pIpxFileObject ) {
+ ObDereferenceObject( pIpxFileObject );
+ pIpxFileObject = NULL;
+ }
+
+ if (IpxHandle) {
+ //
+ // Attach to the redirector's FSP to allow the handle for the
+ // connection to hang around.
+ //
+
+ if (PsGetCurrentProcess() != FspProcess) {
+ ATTACHPROCESS(FspProcess);
+ ZwClose( IpxHandle );
+ KeDetachProcess();
+ } else {
+ ZwClose( IpxHandle );
+ }
+
+ IpxHandle = NULL;
+ }
+ DebugTrace(-1, Dbg, "IpxClose\n", 0);
+
+}
+
+
+NTSTATUS
+SetEventHandler (
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG EventType,
+ IN PVOID pEventHandler,
+ IN PVOID pContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine registers an event handler with a TDI transport provider.
+
+Arguments:
+
+ pIrpC - supplies an Irp among other things.
+
+ pTdiStruc - supplies the handle and both device and file object pointers
+ to the transport.
+
+ IN ULONG EventType, - Supplies the type of event.
+
+ IN PVOID pEventHandler - Supplies the event handler.
+
+ IN PVOID pContext - Supplies the context to be supplied to the event
+ handler.
+
+Return Value:
+
+ NTSTATUS - Final status of the set event operation
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ TdiBuildSetEventHandler(pIrpC->pOriginalIrp,
+ pTdiStruc->pDeviceObject,
+ pTdiStruc->pFileObject,
+ NULL,
+ NULL,
+ EventType,
+ pEventHandler,
+ pContext);
+
+ Status = SubmitTdiRequest(pTdiStruc->pDeviceObject,
+ pIrpC->pOriginalIrp);
+
+ return Status;
+}
+
+
+NTSTATUS
+SubmitTdiRequest (
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine submits a request to TDI and waits for it to complete.
+
+Arguments:
+
+ IN PDevice_OBJECT DeviceObject - Connection or Address handle for TDI request
+ IN PIRP Irp - TDI request to submit.
+
+Return Value:
+
+ NTSTATUS - Final status of request.
+
+--*/
+
+{
+ NTSTATUS Status;
+ KEVENT Event;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "SubmitTdiRequest\n", 0);
+
+ KeInitializeEvent (&Event, NotificationEvent, FALSE);
+
+ IoSetCompletionRoutine(pIrp, CompletionEvent, &Event, TRUE, TRUE, TRUE);
+
+ //
+ // Submit the request
+ //
+
+ Status = IoCallDriver(pDeviceObject, pIrp);
+
+ //
+ // If it failed immediately, return now, otherwise wait.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status);
+ return Status;
+ }
+
+ if (Status == STATUS_PENDING) {
+
+ DebugTrace(+0, Dbg, "Waiting....\n", 0);
+
+ Status = KeWaitForSingleObject(&Event, // Object to wait on.
+ Executive, // Reason for waiting
+ KernelMode, // Processor mode
+ FALSE, // Alertable
+ NULL); // Timeout
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "SubmitTdiRequest could not wait %X\n", Status);
+ return Status;
+ }
+
+ Status = pIrp->IoStatus.Status;
+ }
+
+ DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status);
+
+ return(Status);
+}
+
+
+NTSTATUS
+CompletionEvent(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine does not complete the Irp. It is used to signal to a
+ synchronous part of the driver that it can proceed.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the event associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ DebugTrace( 0, Dbg, "CompletionEvent\n", 0 );
+
+ KeSetEvent((PKEVENT )Context, 0, FALSE);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+
+USHORT
+GetSocketNumber(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to set the Option.
+
+Arguments:
+
+ pIrpC - supplies an Irp among other things.
+
+ pTdiStruc - supplies the handle and both device and file object pointers
+ to the transport.
+
+ Option - supplies the option to set.
+
+Return Value:
+
+ 0 failed otherwise the socket number.
+
+--*/
+{
+ ADDRESS_INFORMATION AddressInfo;
+ NTSTATUS Status;
+ USHORT SocketNumber;
+
+ PAGED_CODE();
+
+ Status = QueryAddressInformation( pIrpC, pTdiStruc, &AddressInfo );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SocketNumber = 0;
+ } else {
+ SocketNumber = AddressInfo.NetworkName.Address[0].Address[0].Socket;
+
+ RtlCopyMemory( &OurAddress,
+ &AddressInfo.NetworkName.Address[0].Address[0],
+ sizeof(TDI_ADDRESS_IPX));
+
+ }
+
+ return( SocketNumber );
+}
+
+
+NTSTATUS
+GetMaximumPacketSize(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PULONG pMaximumPacketSize
+ )
+/*++
+
+Routine Description:
+
+ Query the maximum packet size for this network.
+
+Arguments:
+
+ pIrpContext - supplies an Irp among other things.
+
+ pTdiStruct - supplies the handle and both device and file object pointers
+ to the transport.
+
+ pMaximumPacketSize - Returns the maximum packet size for the network.
+
+Return Value:
+
+ The status of the query.
+
+--*/
+{
+ TDI_PROVIDER_INFO ProviderInfo;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ Status = QueryProviderInformation( pIrpContext, pTdiStruct, &ProviderInfo );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pMaximumPacketSize = ProviderInfo.MaximumLookaheadData;
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+QueryAddressInformation(
+ PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ PADDRESS_INFORMATION AddressInformation
+ )
+{
+ NTSTATUS Status;
+
+ PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress;
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ AddressInformation,
+ sizeof( *AddressInformation ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ TdiBuildQueryInformation(
+ pIrpContext->pOriginalIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionEvent,
+ NULL,
+ TDI_QUERY_ADDRESS_INFO,
+ Mdl);
+
+ Status = SubmitTdiRequest( pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp);
+
+ pIrpContext->pOriginalIrp->MdlAddress = MdlSave;
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return( Status );
+}
+
+
+NTSTATUS
+QueryProviderInformation(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ PTDI_PROVIDER_INFO ProviderInfo
+ )
+{
+ NTSTATUS Status;
+
+ PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress;
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ ProviderInfo,
+ sizeof( *ProviderInfo ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ TdiBuildQueryInformation(
+ pIrpContext->pOriginalIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionEvent,
+ NULL,
+ TDI_QUERY_PROVIDER_INFO,
+ Mdl);
+
+ Status = SubmitTdiRequest(pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp);
+
+ pIrpContext->pOriginalIrp->MdlAddress = MdlSave;
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+SetTransportOption(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG Option
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to set the Option.
+
+Arguments:
+
+ pIrpC - supplies an Irp among other things.
+
+ pTdiStruc - supplies the handle and both device and file object pointers
+ to the transport.
+
+ Option - supplies the option to set.
+
+Return Value:
+
+ 0 success
+
+--*/
+{
+ static struct {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ } SetPacketType = {
+ IPX_ID,
+ 0, // ActionCode
+ 0, // Reserved
+ TRUE, // DatagramOption
+ sizeof(ULONG) // BufferLength
+ };
+
+ KEVENT Event;
+ NTSTATUS Status;
+
+ PIRP pIrp = pIrpC->pOriginalIrp;
+
+ //
+ // Save the original MDL and System buffer address, to restore
+ // after the IRP completes.
+ //
+ // We use both the MDL and SystemBuffer because NWLINK assumes that
+ // we are using SystemBuffer even though we are supposed to use the
+ // MDL to pass a pointer to the action buffer.
+ //
+
+ PMDL MdlSave = pIrp->MdlAddress;
+ PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
+
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ &SetPacketType,
+ sizeof( SetPacketType ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ IPX_Close_Socket( pTdiStruc );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ SetPacketType.Option = Option;
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ KeInitializeEvent (
+ &Event,
+ SynchronizationEvent,
+ FALSE);
+
+ TdiBuildAction(
+ pIrp,
+ pTdiStruc->pDeviceObject,
+ pTdiStruc->pFileObject,
+ CompletionEvent,
+ &Event,
+ Mdl );
+
+ //
+ // Set up the system buffer for NWLINK.
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = &SetPacketType;
+
+ Status = IoCallDriver (pTdiStruc->pDeviceObject, pIrp);
+
+ if ( Status == STATUS_PENDING ) {
+ Status = KeWaitForSingleObject (
+ &Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = pIrp->IoStatus.Status;
+ }
+ }
+
+ //
+ // Now restore the system buffer and MDL address in the IRP
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
+ pIrp->MdlAddress = MdlSave;
+
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return Status;
+}
+
+
+NTSTATUS
+GetNewRoute(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to get a new route.
+
+Arguments:
+
+ pIrpContext - Supplies IRP context information.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ struct {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ ULONG info_netnum;
+ USHORT info_hopcount;
+ USHORT info_netdelay;
+ int info_cardnum;
+ UCHAR info_router[6];
+ } ReRipRequest = {
+ IPX_ID,
+ 0, // ActionCode
+ 0, // Reserved
+ TRUE, // DatagramOption
+ 24 // Buffer length (not including header)
+ };
+
+ KEVENT Event;
+ NTSTATUS Status;
+
+ PIRP pIrp = pIrpContext->pOriginalIrp;
+
+ //
+ // Save the original MDL and System buffer address, to restore
+ // after the IRP completes.
+ //
+ // We use both the MDL and SystemBuffer because NWLINK assumes that
+ // we are using SystemBuffer even though we are supposed to use the
+ // MDL to pass a pointer to the action buffer.
+ //
+
+ PMDL MdlSave = pIrp->MdlAddress;
+ PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
+
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ &ReRipRequest,
+ sizeof( ReRipRequest ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ReRipRequest.Option = MIPX_RERIPNETNUM;
+ ReRipRequest.info_netnum = pIrpContext->pNpScb->ServerAddress.Net;
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ KeInitializeEvent (
+ &Event,
+ SynchronizationEvent,
+ FALSE);
+
+ TdiBuildAction(
+ pIrp,
+ pIrpContext->pNpScb->Server.pDeviceObject,
+ pIrpContext->pNpScb->Server.pFileObject,
+ CompletionEvent,
+ &Event,
+ Mdl );
+
+ //
+ // Set up the system buffer for NWLINK.
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = &ReRipRequest;
+
+ Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp);
+
+ if ( Status == STATUS_PENDING ) {
+ Status = KeWaitForSingleObject (
+ &Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = pIrp->IoStatus.Status;
+ }
+ }
+
+ //
+ // Now restore the system buffer and MDL address in the IRP
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
+ pIrp->MdlAddress = MdlSave;
+
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return Status;
+}
+
+
+NTSTATUS
+GetTickCount(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUSHORT TickCount
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to get a new route.
+
+Arguments:
+
+ pIrpContext - Supplies IRP context information.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ struct {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ IPX_NETNUM_DATA NetNumData;
+ } GetTickCountInput = {
+ IPX_ID,
+ 0, // ActionCode
+ 0, // Reserved
+ TRUE, // DatagramOption
+ sizeof( IPX_NETNUM_DATA) + 2 * sizeof( ULONG )
+ };
+
+ struct _GET_TICK_COUNT_OUTPUT {
+ ULONG Option;
+ IPX_NETNUM_DATA NetNumData;
+ };
+
+ struct _GET_TICK_COUNT_OUTPUT *GetTickCountOutput;
+
+ KEVENT Event;
+ NTSTATUS Status;
+
+ PIRP pIrp = pIrpContext->pOriginalIrp;
+
+ //
+ // Save the original MDL and System buffer address, to restore
+ // after the IRP completes.
+ //
+ // We use both the MDL and SystemBuffer because NWLINK assumes that
+ // we are using SystemBuffer even though we are supposed to use the
+ // MDL to pass a pointer to the action buffer.
+ //
+
+ PMDL MdlSave = pIrp->MdlAddress;
+ PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
+
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ &GetTickCountInput,
+ sizeof( GetTickCountInput ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ GetTickCountInput.Option = MIPX_GETNETINFO;
+ *(PULONG)GetTickCountInput.NetNumData.netnum = pIrpContext->pNpScb->ServerAddress.Net;
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ KeInitializeEvent (
+ &Event,
+ SynchronizationEvent,
+ FALSE);
+
+ TdiBuildAction(
+ pIrp,
+ pIrpContext->pNpScb->Server.pDeviceObject,
+ pIrpContext->pNpScb->Server.pFileObject,
+ CompletionEvent,
+ &Event,
+ Mdl );
+
+ //
+ // Set up the system buffer for NWLINK.
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = &GetTickCountInput;
+
+ Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp);
+
+ if ( Status == STATUS_PENDING ) {
+ Status = KeWaitForSingleObject (
+ &Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = pIrp->IoStatus.Status;
+ }
+ }
+
+ DebugTrace( +0, Dbg, "Get Tick Count, net= %x\n", pIrpContext->pNpScb->ServerAddress.Net );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // HACK-o-rama. Streams and non-streams IPX have different output
+ // buffer formats. For now accept both.
+ //
+
+ if ( IpxTransportName.Length == 32 ) {
+
+ // ISNIPX format
+
+ *TickCount = GetTickCountInput.NetNumData.netdelay;
+ } else {
+
+ // NWLINK format
+
+ GetTickCountOutput = (struct _GET_TICK_COUNT_OUTPUT *)&GetTickCountInput;
+ *TickCount = GetTickCountOutput->NetNumData.netdelay;
+ }
+
+ DebugTrace( +0, Dbg, "Tick Count = %d\n", *TickCount );
+ } else {
+ DebugTrace( +0, Dbg, "GetTickCount failed, status = %X\n", Status );
+ }
+
+ //
+ // Now restore the system buffer and MDL address in the IRP
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
+ pIrp->MdlAddress = MdlSave;
+
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return Status;
+}
+
+#ifndef QFE_BUILD
+
+static PIRP LineChangeIrp = NULL;
+
+
+NTSTATUS
+SubmitLineChangeRequest(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to get a new route.
+
+Arguments:
+
+ pIrpContext - Supplies IRP context information.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ struct _LINE_CHANGE {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ } *LineChangeInput;
+
+ PIRP pIrp;
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ LineChangeInput = ALLOCATE_POOL( NonPagedPool, sizeof( struct _LINE_CHANGE ) );
+
+ //
+ // Complete initialization of the request, and allocate and build an
+ // MDL for the request input buffer.
+ //
+
+ LineChangeInput->Header.TransportId = IPX_ID;
+ LineChangeInput->Header.ActionCode = 0;
+ LineChangeInput->Header.Reserved = 0;
+ LineChangeInput->DatagramOption = 2;
+ LineChangeInput->BufferLength = 2 * sizeof( ULONG );
+ LineChangeInput->Option = MIPX_LINECHANGE;
+
+ Mdl = ALLOCATE_MDL(
+ LineChangeInput,
+ sizeof( *LineChangeInput ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ FREE_POOL( LineChangeInput );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE );
+
+ if ( pIrp == NULL ) {
+ FREE_POOL( LineChangeInput );
+ FREE_MDL( Mdl );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Remember this IRP so that we can cancel it.
+ //
+
+ LineChangeIrp = pIrp;
+
+ MmBuildMdlForNonPagedPool( Mdl );
+
+ //
+ // Build and submit a TDI request packet.
+ //
+
+ TdiBuildAction(
+ pIrp,
+ pIpxDeviceObject,
+ pIpxFileObject,
+ CompletionLineChange,
+ NULL,
+ Mdl );
+
+ IoCallDriver ( pIpxDeviceObject, pIrp );
+}
+
+
+
+NTSTATUS
+CompletionLineChange(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when the transport completes a line change IRP.
+ This means that we have switched nets, and that we should mark
+ all of our servers disconnected.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - unused.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PMDL Mdl;
+ PWORK_QUEUE_ITEM WorkQueueItem;
+
+ DebugTrace( 0, Dbg, "CompletionLineChange\n", 0 );
+
+ Mdl = Irp->MdlAddress;
+
+ if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) {
+ FREE_POOL( Mdl->MappedSystemVa );
+ FREE_MDL( Mdl );
+ FREE_IRP( Irp );
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+ }
+
+ //
+ // If the scavenger is running, simply make a note that
+ // we need to do this when it is finished.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ if ( WorkerRunning ) {
+
+ if ( ( DelayedProcessLineChange != FALSE ) &&
+ ( DelayedLineChangeIrp != NULL ) ) {
+
+ //
+ // We've already got a line change. Dump this one.
+ //
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ DebugTrace( 0, Dbg, "Dumping an additional line change request.\n", 0 );
+
+ FREE_POOL( Mdl->MappedSystemVa );
+ FREE_MDL( Mdl );
+ FREE_IRP( Irp );
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Delaying a line change request.\n", 0 );
+
+ DelayedProcessLineChange = TRUE;
+ DelayedLineChangeIrp = Irp;
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ } else {
+
+ //
+ // Don't let the scavenger start up while we're running.
+ //
+
+ WorkerRunning = TRUE;
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+ }
+
+ WorkQueueItem = ALLOCATE_POOL( NonPagedPool, sizeof( *WorkQueueItem ) );
+ if ( WorkQueueItem == NULL ) {
+ FREE_POOL( Mdl->MappedSystemVa );
+ FREE_MDL( Mdl );
+ FREE_IRP( Irp );
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+ }
+
+ //
+ // Use the user buffer field as a convenient place to remember where
+ // the address of the WorkQueueItem. We can get away with this since
+ // we don't let this IRP complete.
+ //
+
+ Irp->UserBuffer = WorkQueueItem;
+
+ //
+ // Process the line change in the FSP.
+ //
+
+ ExInitializeWorkItem( WorkQueueItem, FspProcessLineChange, Irp );
+ ExQueueWorkItem( WorkQueueItem, DelayedWorkQueue );
+
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+}
+
+VOID
+FspProcessLineChange(
+ IN PVOID Context
+ )
+{
+ PIRP Irp;
+ ULONG ActiveHandles;
+
+ NwReferenceUnlockableCodeSection();
+
+ Irp = (PIRP)Context;
+
+ //
+ // Free the work queue item
+ //
+
+ FREE_POOL( Irp->UserBuffer );
+ Irp->UserBuffer = NULL;
+
+ //
+ // Invalid all remote handles
+ //
+
+ ActiveHandles = NwInvalidateAllHandles(NULL, NULL);
+
+ //
+ // Now that we're done walking all the servers, it's safe
+ // to let the scavenger run again.
+ //
+
+ WorkerRunning = FALSE;
+
+ //
+ // Resubmit the IRP
+ //
+
+ TdiBuildAction(
+ Irp,
+ pIpxDeviceObject,
+ pIpxFileObject,
+ CompletionLineChange,
+ NULL,
+ Irp->MdlAddress );
+
+ IoCallDriver ( pIpxDeviceObject, Irp );
+
+ NwDereferenceUnlockableCodeSection ();
+ return;
+}
+#endif
+
diff --git a/private/nw/rdr/kdext/dirs b/private/nw/rdr/kdext/dirs
new file mode 100644
index 000000000..e6ecf7a47
--- /dev/null
+++ b/private/nw/rdr/kdext/dirs
@@ -0,0 +1,26 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+DIRS=ntsd \
+ windbg
+
+OPTIONAL_DIRS=
diff --git a/private/nw/rdr/kdext/ntsd/makefile b/private/nw/rdr/kdext/ntsd/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/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/rdr/kdext/ntsd/nw.def b/private/nw/rdr/kdext/ntsd/nw.def
new file mode 100644
index 000000000..9d9dae6d8
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/nw.def
@@ -0,0 +1,15 @@
+DESCRIPTION 'NT Netware Redirector KD extensions'
+
+EXPORTS
+ nwdump
+ logonlist
+ serverlist
+ trace
+ reftrace
+ traceflags
+ help
+ vcblist
+ fcblist
+ icblist
+ irplist
+ credlist
diff --git a/private/nw/rdr/kdext/ntsd/nw.rc b/private/nw/rdr/kdext/ntsd/nw.rc
new file mode 100644
index 000000000..23f42b167
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/nw.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 i386 KD Debugger Extensions DLL"
+#define VER_INTERNALNAME_STR "Nw.DLL"
+#define VER_ORIGINALFILENAME_STR "Nw.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/rdr/kdext/ntsd/sources b/private/nw/rdr/kdext/ntsd/sources
new file mode 100644
index 000000000..da56f0039
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/sources
@@ -0,0 +1,49 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=rdr
+
+TARGETNAME=nw
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib
+
+
+DLLBASE=0x1010000
+
+INCLUDES=..\..;..\..\..\inc;$(NTOS_ROOT)\inc;$(_NTROOT)\private\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=..\nwrdrkd.c \
+ nw.rc
+
+UMTYPE=console
+OPTIONAL_NTTEST=
+
diff --git a/private/nw/rdr/kdext/nwrdrkd.c b/private/nw/rdr/kdext/nwrdrkd.c
new file mode 100644
index 000000000..0b1abeef2
--- /dev/null
+++ b/private/nw/rdr/kdext/nwrdrkd.c
@@ -0,0 +1,2223 @@
+/*++
+
+NwRdr Kernel Debugger Extensions
+Copyright (c) 1995 Microsoft Corporation
+
+Abstract:
+
+ NW Redirector Kernel Debugger extensions.
+
+ This module contains a set of useful kernel debugger
+ extensions for the NT nw redirector.
+
+Author:
+
+ Cory West <corywest>, 09-Jan-1994
+
+--*/
+
+#include "procs.h"
+#include "nodetype.h"
+
+#include <ntkdexts.h>
+#include <string.h>
+#include <stdlib.h>
+
+//
+// Function prototypes.
+//
+
+VOID
+DumpScbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ );
+
+VOID
+DumpFcbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ );
+
+//
+// Define some macros for simplicity.
+//
+
+#define GET_DWORD( pDest, addr ) \
+ (lpExtensionApis->lpReadVirtualMemRoutine)((LPVOID)(addr), pDest, 4, NULL)
+#define GET_WORD( pDest, addr ) \
+ (lpExtensionApis->lpReadVirtualMemRoutine)((LPVOID)(addr), pDest, 2, NULL)
+#define GET_STRING( pDest, string ) \
+ (lpExtensionApis->lpReadVirtualMemRoutine)(string.Buffer, pDest, \
+ string.Length, NULL); pDest[ string.Length/2 ] = L'\0'
+
+#define printf lpExtensionApis->lpOutputRoutine
+#define getmem lpExtensionApis->lpReadVirtualMemRoutine
+#define getexpr lpExtensionApis->lpGetExpressionRoutine
+
+#ifdef WINDBG
+#define getsymaddr( string ) ((lpExtensionApis->lpGetExpressionRoutine))( "&"##string )
+#else
+#define getsymaddr lpExtensionApis->lpGetExpressionRoutine
+#endif
+
+VOID
+help(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function prints out usage for the nw debugger extensions.
+
+--*/
+{
+ printf( "---------------------------------------------------------------------------\n");
+ printf( "NwRdr Debugger Extensions:\n\n");
+
+ printf( "Top Level Functions:\n\n");
+
+ printf( "serverlist(void) - List the servers that the redirector knows.\n");
+ printf( "logonlist(void) - List the users that are logged on.\n");
+ printf( "trace(void) - Display the trace buffer.\n");
+ printf( "nwdump(virtual addr) - Display the object at the given virtual address.\n");
+ printf( " (This function knows how to dump all NwRdr data\n");
+ printf( " structures.)\n");
+ printf( "help(void) - Display this message.\n\n");
+
+ printf( "List Management Functions:\n\n");
+
+ printf( "vcblist(scb*, npscb*) - Given a pointer to any of the specified objects,\n");
+ printf( " this function dumps the VCB list for that server.\n");
+ printf( "irplist(scb*, npscb*) - Given a pointer to any of the specified objects,\n");
+ printf( " this function dumps the IRP list for that server.\n");
+ printf( "fcblist(vcb*) - Given a pointer to a VCB, this function dumps\n");
+ printf( " the FCB/DCB list for that VCB.\n");
+ printf( "icblist(scb*, npscb*,\n");
+ printf( " fcb*, dcb*,\n");
+ printf( " npfcb*) - Given a pointer to any of the specified objects,\n");
+ printf( " function dumps the ICB list for that object.\n");
+ printf( "---------------------------------------------------------------------------\n");
+}
+
+VOID
+traceflags(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function prints out the trace flag values.
+
+--*/
+{
+ printf( "DEBUG_TRACE_CLEANUP (0x00000001)\n");
+ printf( "DEBUG_TRACE_CLOSE (0x00000002)\n");
+ printf( "DEBUG_TRACE_CLEANUP (0x00000001)\n");
+ printf( "DEBUG_TRACE_CLOSE (0x00000002)\n");
+ printf( "DEBUG_TRACE_CREATE (0x00000004)\n");
+ printf( "DEBUG_TRACE_FSCTRL (0x00000008)\n");
+ printf( "DEBUG_TRACE_IPX (0x00000010)\n");
+ printf( "DEBUG_TRACE_LOAD (0x00000020)\n");
+ printf( "DEBUG_TRACE_EXCHANGE (0x00000040)\n");
+ printf( "DEBUG_TRACE_FILOBSUP (0x00000080)\n");
+ printf( "DEBUG_TRACE_STRUCSUP (0x00000100)\n");
+ printf( "DEBUG_TRACE_FSP_DISPATCHER (0x00000200)\n");
+ printf( "DEBUG_TRACE_FSP_DUMP (0x00000400)\n");
+ printf( "DEBUG_TRACE_WORKQUE (0x00000800)\n");
+ printf( "DEBUG_TRACE_UNWIND (0x00001000)\n");
+ printf( "DEBUG_TRACE_CATCH_EXCEPTIONS (0x00002000)\n");
+ printf( "DEBUG_TRACE_FILEINFO (0x00008000)\n");
+ printf( "DEBUG_TRACE_DIRCTRL (0x00010000)\n");
+ printf( "DEBUG_TRACE_CONVERT (0x00020000)\n");
+ printf( "DEBUG_TRACE_WRITE (0x00040000)\n");
+ printf( "DEBUG_TRACE_READ (0x00080000)\n");
+ printf( "DEBUG_TRACE_VOLINFO (0x00100000)\n");
+ printf( "DEBUG_TRACE_LOCKCTRL (0x00200000)\n");
+ printf( "DEBUG_TRACE_USERNCP (0x00400000)\n");
+ printf( "DEBUG_TRACE_SECURITY (0x00800000)\n");
+ printf( "DEBUG_TRACE_CACHE (0x01000000)\n");
+ printf( "DEBUG_TRACE_LIP (0x02000000)\n");
+ printf( "DEBUG_TRACE_MDL (0x04000000)\n");
+ printf( "DEBUG_TRACE_NDS (0x10000000)\n");
+ printf( "DEBUG_TRACE_SCAVENGER (0x40000000)\n");
+ printf( "DEBUG_TRACE_TIMER (0x80000000)\n");
+}
+
+//
+// Internal helper routines to convert numerical data into symbolic data.
+//
+
+NODE_TYPE_CODE
+GetNodeType(
+ DWORD objAddr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ Given the address of an object, this function will
+ attempt to get the node type code for that object.
+
+--*/
+{
+
+ NODE_TYPE_CODE ntc;
+ GET_WORD( &ntc, objAddr );
+ return ntc;
+
+}
+
+LPSTR
+RcbStateToString(
+ DWORD State
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts the RCB state from a
+ DWORD to a readable text string.
+
+Arguments:
+
+ DWORD State - The DWORD RCB state.
+
+Return Value:
+
+ LPSTR containing the readable text string.
+
+--*/
+{
+ switch ( State ) {
+
+ case RCB_STATE_STOPPED:
+ return("RCB_STATE_STOPPED");
+
+
+ case RCB_STATE_STARTING:
+ return("RCB_STATE_STARTING");
+
+ case RCB_STATE_NEED_BIND:
+ return("RCB_STATE_NEED_BIND");
+
+ case RCB_STATE_RUNNING:
+ return("RCB_STATE_RUNNING");
+
+ case RCB_STATE_SHUTDOWN:
+ return("RCB_STATE_SHUTDOWN");
+
+ default:
+ return("(state unknown)" );
+ }
+}
+
+LPSTR
+ScbStateToString(
+ DWORD State
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts the SCB state from a
+ DWORD to a readable text string.
+
+Arguments:
+
+ DWORD State - The DWORD SCB state.
+
+Return Value:
+
+ LPSTR containing the readable text string.
+
+--*/
+{
+ switch ( State ) {
+
+ case SCB_STATE_ATTACHING:
+ return("SCB_STATE_ATTACHING" );
+
+ case SCB_STATE_IN_USE:
+ return("SCB_STATE_IN_USE" );
+
+ case SCB_STATE_DISCONNECTING:
+ return("SCB_STATE_DISCONNECTING" );
+
+ case SCB_STATE_FLAG_SHUTDOWN:
+ return("SCB_STATE_FLAG_SHUTDOWN" );
+
+ case SCB_STATE_RECONNECT_REQUIRED:
+ return("SCB_STATE_RECONNECT_REQD" );
+
+ case SCB_STATE_LOGIN_REQUIRED:
+ return("SCB_STATE_LOGIN_REQUIRED" );
+
+ case SCB_STATE_TREE_SCB:
+ return("SCB_STATE_TREE_SCB" );
+
+ default:
+ return("(state unknown)" );
+ }
+}
+
+LPSTR
+IcbStateToString(
+ DWORD State
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts the ICB state from a
+ DWORD to a readable text string.
+
+--*/
+{
+ switch ( State ) {
+
+ case ICB_STATE_OPEN_PENDING:
+ return("ICB_STATE_OPEN_PENDING" );
+
+ case ICB_STATE_OPENED:
+ return("ICB_STATE_OPENED" );
+
+ case ICB_STATE_CLEANED_UP:
+ return("ICB_STATE_CLEANED_UP" );
+
+ case ICB_STATE_CLOSE_PENDING:
+ return("ICB_STATE_CLOSE_PENDING" );
+
+ default:
+ return("(state unknown)" );
+ }
+}
+
+VOID
+PrintIrpContextFlags(
+ ULONG Flags,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ Print out the flags that are set in the IRP_CONTEXT flags.
+
+--*/
+{
+
+ if ( Flags & IRP_FLAG_IN_FSD )
+ printf( "\tIRP_FLAG_IN_FSD\n" );
+
+ if ( Flags & IRP_FLAG_ON_SCB_QUEUE )
+ printf( "\tIRP_FLAG_ON_SCB_QUEUE\n" );
+
+ if ( Flags & IRP_FLAG_SEQUENCE_NO_REQUIRED )
+ printf( "\tIRP_FLAG_SEQUENCE_NO_REQUIRED\n" );
+
+ if ( Flags & IRP_FLAG_SIGNAL_EVENT )
+ printf( "\tIRP_FLAG_SIGNAL_EVENT\n" );
+
+ if ( Flags & IRP_FLAG_RETRY_SEND )
+ printf( "\tIRP_FLAG_RETRY_SEND\n" );
+
+ if ( Flags & IRP_FLAG_RECONNECTABLE )
+ printf( "\tIRP_FLAG_RECONNECTABLE\n" );
+
+ if ( Flags & IRP_FLAG_RECONNECT_ATTEMPT )
+ printf( "\tIRP_FLAG_RECONNECT_ATTEMPT\n" );
+
+ if ( Flags & IRP_FLAG_BURST_REQUEST )
+ printf( "\tIRP_FLAG_BURST_REQUEST\n" );
+
+ if ( Flags & IRP_FLAG_BURST_PACKET )\
+ printf( "\tIRP_FLAG_BURST_PACKET\n" );
+
+ if ( Flags & IRP_FLAG_NOT_OK_TO_RECEIVE )
+ printf( "\tIRP_FLAG_NOT_OK_TO_RECEIVE\n" );
+
+ if ( Flags & IRP_FLAG_REROUTE_ATTEMPTED )
+ printf( "\tIRP_FLAG_REROUTE_ATTEMPTED\n" );
+
+ if ( Flags & IRP_FLAG_BURST_WRITE )
+ printf( "\tIRP_FLAG_BURST_WRITE\n" );
+
+ if ( Flags & IRP_FLAG_SEND_ALWAYS )
+ printf( "\tIRP_FLAG_SEND_ALWAYS\n" );
+
+ if ( Flags & IRP_FLAG_FREE_RECEIVE_MDL )
+ printf( "\tIRP_FLAG_FREE_RECEIVE_MDL\n" );
+
+ if ( Flags & IRP_FLAG_NOT_SYSTEM_PACKET )
+ printf( "\tIRP_FLAG_NOT_SYSTEM_PACKET\n" );
+
+ if ( Flags & IRP_FLAG_NOCONNECT )
+ printf( "\tIRP_FLAG_NOCONNECT\n" );
+
+}
+
+VOID
+PrintNpFcbFlags(
+ ULONG Flags,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ Print out the flags that are set in the IRP_CONTEXT flags.
+
+--*/
+{
+
+ if ( Flags & FCB_FLAGS_DELETE_ON_CLOSE )
+ printf( "\tFCB_FLAGS_DELETE_ON_CLOSE\n" );
+
+ if ( Flags & FCB_FLAGS_TRUNCATE_ON_CLOSE )
+ printf( "\tFCB_FLAGS_TRUNCATE_ON_CLOSE\n" );
+
+ if ( Flags & FCB_FLAGS_PAGING_FILE )
+ printf( "\tFCB_FLAGS_PAGING_FILE\n" );
+
+ if ( Flags & FCB_FLAGS_PREFIX_INSERTED )
+ printf( "\tFCB_FLAGS_PREFIX_INSERTED\n" );
+
+ if ( Flags & FCB_FLAGS_FORCE_MISS_IN_PROGRESS )
+ printf( "\tFCB_FLAGS_FORCE_MISS_IN_PROGRESS\n" );
+
+ if ( Flags & FCB_FLAGS_ATTRIBUTES_ARE_VALID )
+ printf( "\tFCB_FLAGS_ATTRIBUTES_ARE_VALID\n" );
+
+ if ( Flags & FCB_FLAGS_LONG_NAME )
+ printf( "\tFCB_FLAGS_LONG_NAME\n" );
+}
+
+LPSTR
+PacketToString(
+ UINT pt
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts a PACKET_TYPE to
+ a readable text string.
+
+--*/
+{
+
+ switch ( pt ) {
+
+ case SAP_BROADCAST:
+ return "SAP_BROADCAST";
+ case NCP_CONNECT:
+ return "NCP_CONNECT";
+ case NCP_FUNCTION:
+ return "NCP_FUNCTION";
+ case NCP_SUBFUNCTION:
+ return "NCP_SUBFUNCTION";
+ case NCP_DISCONNECT:
+ return "NCP_DISCONNECT";
+ case NCP_BURST:
+ return "NCP_BURST";
+ case NCP_ECHO:
+ return "NCP_ECHO";
+ default:
+ return "(packet type unknown)";
+ }
+
+}
+
+//
+// The internal object functions for the nwdump() routine.
+// These functions must receive good pointers; they are
+// neither smart, nor exported.
+//
+
+VOID
+DumpScb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+/*++
+
+ This function takes the address of the pageable portion
+ of an SCB and a pointer to a debugger extension interface
+ block. It prints out the information in the SCB and
+ the corresponding non-pageable SCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ SCB Scb;
+
+ // Read it.
+
+ b = getmem((PVOID)addr, &Scb, sizeof( Scb ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the pageable scb>\n");
+ return;
+ }
+ printf( "-----------------------------SCB at %08lx-------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_SCB\n" );
+ printf( "NodeByteSize : %d\n", Scb.NodeByteSize );
+ printf( "pNpScb Addr : %08lx\n", Scb.pNpScb );
+ printf( "Version : %d\\%d\n", Scb.MajorVersion, Scb.MinorVersion );
+ printf( "VcbList : %08lx (LIST_ENTRY, VCB)\n", addr + FIELD_OFFSET( SCB, ScbSpecificVcbQueue ));
+ printf( "VcbCount : %d\n", Scb.VcbCount );
+ printf( "IcbList : %08lx (LIST_ENTRY, ICB)\n", addr + FIELD_OFFSET( SCB, IcbList ));
+ printf( "IcbCount : %d\n", Scb.IcbCount );
+ printf( "OpenNdsStreams : %d\n", Scb.OpenNdsStreams );
+ printf( "UserUid : %08lx %08lx\n", Scb.UserUid.HighPart, Scb.UserUid.LowPart );
+ printf( "OpenFileCount : %d\n", Scb.OpenFileCount );
+
+ b = GET_STRING( Buffer, Scb.UidServerName );
+ if ( b ) {
+ printf( "UidServerName : %ws\n", Buffer );
+ } else {
+ printf( "UidServerName : (unreadable)\n");
+ }
+
+ b = GET_STRING( Buffer, Scb.NdsTreeName );
+ if ( b ) {
+ printf( "NDS Tree Name : %ws\n", Buffer );
+ } else {
+ printf( "Nds Tree Name : (none)\n");
+ }
+
+ b = GET_STRING( Buffer, Scb.UnicodeUid );
+
+ if ( b ) {
+ printf( "UnicodeUid : %ws\n", Buffer );
+ } else {
+ printf( "UnicodeUid : (unreadable)\n");
+ }
+
+
+ b = GET_STRING( Buffer, Scb.UserName );
+
+ if ( b ) {
+ printf( "User name : %ws\n", Buffer );
+ } else {
+ printf( "User name : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Scb.Password );
+
+ if ( b ) {
+ printf( "Password : %ws\n", Buffer );
+ } else {
+ printf( "Password : (unreadable)\n" );
+ }
+
+ printf( "PreferredServer : %s\n", Scb.PreferredServer ? "TRUE" : "FALSE" );
+ printf( "MessageWaiting : %s\n", Scb.MessageWaiting ? "TRUE" : "FALSE" );
+ printf( "AttachCount : %d\n", Scb.AttachCount);
+
+ // What about the drive map?
+
+ // Dump both parts.
+ if ( first )
+ DumpScbNp( (DWORD)Scb.pNpScb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n");
+
+ return;
+}
+
+VOID
+DumpScbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+/*++
+
+ This function takes the address of the nonpageable
+ portion of an SCB and a pointer to a debugger extension
+ interface block. It prints out the information in the
+ nonpageable SCB and the corresponding pageable SCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ NONPAGED_SCB NpScb;
+
+ // Read it.
+
+ b = getmem( (PVOID)addr, &NpScb, sizeof( NpScb ), NULL );
+ if ( b == 0 ) {
+ printf("<could not read the nonpageable scb>\n");
+ return;
+ }
+
+ printf( "------------------------Non-Pageable SCB at %08lx-----------------------\n", addr);
+ printf( "NodeTypeCode : NW_NTC_SCBNP\n" );
+ printf( "NodeByteSize : %d\n", NpScb.NodeByteSize );
+
+ b = GET_STRING( Buffer, NpScb.ServerName );
+ if ( b ) {
+ printf( "ServerName : %ws\n", Buffer );
+ } else {
+ printf( "ServerName : (unreadable)\n" );
+ }
+
+ printf( "pScb Addr : %08lx\n", NpScb.pScb );
+ printf( "Reference Count : %08lx\n", NpScb.Reference );
+ printf( "State : %s\n", ScbStateToString( NpScb.State ));
+ printf( "Last Used Time : %08lx %08lx\n", NpScb.LastUsedTime.HighPart, NpScb.LastUsedTime.LowPart );
+ printf( "Sending : %s\n", NpScb.Sending ? "TRUE" : "FALSE" );
+ printf( "Receiving : %s\n", NpScb.Receiving ? "TRUE" : "FALSE" );
+ printf( "Ok To Receive : %s\n", NpScb.OkToReceive ? "TRUE" : "FALSE" );
+ printf( "PageAlign : %s\n", NpScb.PageAlign ? "TRUE" : "FALSE" );
+ printf( "Scblinks : %08lx (LIST_ENTRY, NPSCB)\n", addr + FIELD_OFFSET( NONPAGED_SCB, ScbLinks ));
+ printf( "Requests : %08lx (LIST_ENTRY, NPSCB)\n", addr + FIELD_OFFSET( NONPAGED_SCB, Requests ));
+ printf( "------------------------------Transport Info-------------------------------\n" );
+ printf( "TickCount : %d\n", NpScb.TickCount );
+ printf( "RetryCount : %d\n", NpScb.RetryCount );
+ printf( "Timeout : %d\n", NpScb.TimeOut );
+ printf( "SequenceNo : %d\n", NpScb.SequenceNo );
+ printf( "ConnectionNo : %d\n", NpScb.ConnectionNo );
+ printf( "ConnectionNoHi : %d\n", NpScb.ConnectionNoHigh );
+ printf( "ConnectionStat : %d\n", NpScb.ConnectionStatus );
+ printf( "MaxTimeOut : %d\n", NpScb.MaxTimeOut );
+ printf( "BufferSize : %d\n", NpScb.BufferSize );
+ printf( "TaskNo : %d\n", NpScb.TaskNo );
+ printf( "Spin lock : %s\n", NpScb.NpScbSpinLock == 0 ? "Released" : "Acquired " );
+ printf( "LIP Data Speed : %d\n", NpScb.LipDataSpeed );
+ printf( "---------------------------Burst Mode Parameters---------------------------\n");
+ printf( "SourceConnId : %08lx\n", NpScb.SourceConnectionId );
+ printf( "DestConnId : %08lx\n", NpScb.DestinationConnectionId );
+ printf( "MaxPacketSize : %d\n", NpScb.MaxPacketSize );
+ printf( "MaxSendSize : %ld\n", NpScb.MaxSendSize );
+ printf( "MaxReceiveSize : %ld\n", NpScb.MaxReceiveSize );
+ printf( "SendBMEnable : %s\n", NpScb.SendBurstModeEnabled ? "TRUE" : "FALSE" );
+ printf( "ReceiveBMEnable : %s\n", NpScb.ReceiveBurstModeEnabled ? "TRUE" : "FALSE" );
+ printf( "BurstSequenceNo : %d\n", NpScb.BurstSequenceNo );
+ printf( "BurstRequestNo : %d\n", NpScb.BurstRequestNo );
+ printf( "BurstSendDelay : Good %d,\tCurrent %d,\tBad %d\n", NpScb.NwGoodSendDelay, NpScb.NwSendDelay, NpScb.NwBadSendDelay );
+ printf( "BurstReceiveDelay : Good %d,\tCurrent %d,\tBad %d\n", NpScb.NwGoodReceiveDelay, NpScb.NwReceiveDelay, NpScb.NwBadReceiveDelay );
+ printf( "BurstSuccessCount : Send %d, Receive %d\n", NpScb.SendBurstSuccessCount, NpScb.ReceiveBurstSuccessCount );
+ printf( "--------------------------Send Delays and Timeouts-------------------------\n" );
+ printf( "SendTimeout : %d\n", NpScb.SendTimeout );
+ printf( "TotalWaitTime : %d\n", NpScb.TotalWaitTime );
+ printf( "NwLoopTime : %d\n", NpScb.NwLoopTime );
+ printf( "NwSingleBurst : %d\n", NpScb.NwSingleBurstPacketTime );
+ printf( "NwMaxSendDelay : %d\n", NpScb.NwMaxSendDelay );
+ printf( "NwGoodSendDelay : %d\n", NpScb.NwGoodSendDelay );
+ printf( "NwBadSendDelay : %d\n", NpScb.NwBadSendDelay );
+ printf( "BurstDataWritten : %d\n", NpScb.BurstDataWritten );
+ printf( "NwMaxReceiveDelay : %d\n", NpScb.NwMaxReceiveDelay );
+ printf( "NwReceiveDelay : %d\n", NpScb.NwReceiveDelay );
+ printf( "NwGoodReceiveDelay : %d\n", NpScb.NwGoodReceiveDelay );
+ printf( "NwBadReceiveDelay : %d\n", NpScb.NwBadReceiveDelay );
+ printf( "CurrentBurstDelay : %d\n", NpScb.CurrentBurstDelay );
+ printf( "NtSendDelay : %08lx %08lx\n", NpScb.NtSendDelay.HighPart, NpScb.NtSendDelay.LowPart );
+ printf( "NwNextEventTime : %08lx %08lx\n", NpScb.NwNextEventTime.HighPart, NpScb.NwNextEventTime.LowPart );
+
+ // Spin locks? Transport and TDI info?
+
+ // Dump Both Parts.
+ if ( first )
+ DumpScb( (DWORD)NpScb.pScb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n" );
+
+ return;
+}
+
+VOID
+DumpFcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+/*++
+
+ This function takes the address of an FCB or DCB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the FCB or DCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ FCB Fcb;
+
+ b = getmem( (PVOID)addr, &Fcb, sizeof( Fcb ), NULL );
+ if ( b == 0 ) {
+ printf("<could not read the fcb or dcb>\n");
+ return;
+ }
+
+ if (Fcb.NodeTypeCode == NW_NTC_FCB) {
+ printf( "----------------------------FCB at %08lx--------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_FCB\n" );
+ } else {
+ printf( "----------------------------DCB at %08lx--------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_DCB\n" );
+ }
+
+ b = GET_STRING( Buffer, Fcb.FullFileName );
+ if ( b ) {
+ printf( "FullFileName : %ws\n", Buffer );
+ } else {
+ printf( "FullFileName : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Fcb.RelativeFileName );
+ if ( b ) {
+ printf( "RelativeFileName : %ws\n", Buffer );
+ } else {
+ printf( "RelativeFileName : (unreadable)\n" );
+ }
+ printf( "VCB Addr : %08lx\n", Fcb.Vcb );
+ printf( "SCB Addr : %08lx\n", Fcb.Scb );
+ printf( "NpFcb Addr : %08lx\n", Fcb.NonPagedFcb );
+ printf( "LastModifiedDate : %d\n", Fcb.LastModifiedDate );
+ printf( "LastModifiedTime : %d\n", Fcb.LastModifiedTime );
+ printf( "CreationDate : %d\n", Fcb.CreationDate );
+ printf( "CreationTime : %d\n", Fcb.CreationTime );
+ printf( "LastAccessDate : %d\n", Fcb.LastAccessDate );
+ printf( "State : %d\n", Fcb.State );
+ printf( "Flags : %d\n", Fcb.Flags );
+
+ // SHARE_ACCESS?
+
+ printf( "FcbListEntry : %08lx (LIST_ENTRY, FCB)\n", addr + FIELD_OFFSET( FCB, FcbListEntry ));
+ printf( "IcbListEntry : %08lx (LIST_ENTRY, ICB)\n", addr + FIELD_OFFSET( FCB, IcbList ));
+ printf( "IcbCount : %d\n", Fcb.IcbCount );
+ printf( "LastReadOffset : %d\n", Fcb.LastReadOffset );
+ printf( "LastReadSize : %d\n", Fcb.LastReadSize );
+
+ // Dump both parts.
+ if ( first )
+ DumpFcbNp( (DWORD)Fcb.NonPagedFcb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpVcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a VCB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the VCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ VCB Vcb;
+
+ // Read it.
+
+ b = getmem( (PVOID)addr, &Vcb, sizeof( Vcb ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the vcb>\n");
+ return;
+ }
+
+ printf( "------------------------------VCB at %08lx------------------------------\n", addr);
+ printf( "NodeTypeCode : NW_NTC_VCB\n" );
+ printf( "NodeByteSize : %d\n", Vcb.NodeByteSize );
+ printf( "Reference Count : %08lx\n", Vcb.Reference );
+ printf( "Last Used Time : %08lx %08lx\n", Vcb.LastUsedTime.HighPart, Vcb.LastUsedTime.LowPart );
+ printf( "GlobalVcbListEntry : %08lx (LIST_ENTRY, VCB)\n", addr + FIELD_OFFSET( VCB, GlobalVcbListEntry) );
+ printf( "SequenceNumber : %d\n", Vcb.SequenceNumber );
+
+ b = GET_STRING( Buffer, Vcb.Name );
+ if ( b ) {
+ printf( "VolumeName : %ws\n", Buffer );
+ } else {
+ printf( "VolumeName : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Vcb.ConnectName );
+ if ( b ) {
+ printf( "ConnectName : %ws\n", Buffer );
+ } else {
+ printf( "ConnectName : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Vcb.ShareName );
+ if ( b ) {
+ printf( "NW ShareName : %ws\n", Buffer );
+ } else {
+ printf( "NW ShareName : (unreadable)\n" );
+ }
+
+ if ( !Vcb.Flags & VCB_FLAG_PRINT_QUEUE ) {
+ printf( "VolumeNumber : %d\n", Vcb.Specific.Disk.VolumeNumber );
+ printf( "LongNameSpace : %d\n", Vcb.Specific.Disk.LongNameSpace );
+ printf( "Handle : %d\n", Vcb.Specific.Disk.Handle );
+ } else {
+ printf( "QueueId : %d\n", Vcb.Specific.Print.QueueId );
+ }
+
+ if ( Vcb.DriveLetter != 0) {
+ printf( "Drive letter : %wc:\n", Vcb.DriveLetter );
+ } else {
+ printf( "Drive letter : UNC\n" );
+ }
+
+ printf( "Scb Addr : %08lx\n", Vcb.Scb );
+ printf( "VcbListEntry : %08lx (LIST_ENTRY, VCB)\n", addr + FIELD_OFFSET( VCB, VcbListEntry) );
+ printf( "FcbListEntry : %08lx (LIST_ENTRY, FCB)\n", addr + FIELD_OFFSET(VCB, FcbList) );
+ printf( "OpenFileCount : %d\n", Vcb.OpenFileCount );
+ printf( "Flags : %08lx\n", Vcb.Flags );
+ printf( "---------------------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpIcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of an ICB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the ICB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b, icbscb;
+ ICB Icb;
+ UINT hb;
+
+ b = getmem( (PVOID)addr, &Icb, sizeof( Icb ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the icb>\n");
+ return;
+ }
+
+ icbscb = (Icb.NodeTypeCode == NW_NTC_ICB_SCB);
+
+ if ( icbscb ) {
+ printf( "---------------------------ICB_SCB at %08lx-----------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_ICB_SCB\n" );
+ } else {
+ printf( "-----------------------------ICB at %08lx-------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_ICB\n" );
+ }
+
+ printf( "NodeByteSize : %d\n", Icb.NodeByteSize );
+ printf( "ListEntry : %08lx\n", Icb.ListEntry );
+
+ if (icbscb ) {
+ printf( "SuperType Addr : %08lx (SCB)\n", Icb.SuperType.Scb );
+ } else {
+ printf( "SuperType Addr : %08lx (FCB)\n", Icb.SuperType.Fcb );
+ printf( "NpFcb Addr : %08lx\n", Icb.NpFcb );
+ }
+
+ printf( "State : %s\n", IcbStateToString(Icb.State) );
+ printf( "HasRemoteHandle : %s\n", Icb.HasRemoteHandle ? "TRUE" : "FALSE" );
+
+ if ( Icb.HasRemoteHandle ) {
+ printf( "Handle : " );
+ for ( hb = 0; hb < 6; hb++ ) {
+ printf( "%c ", (Icb.Handle)[hb]);
+ }
+ printf( "\n");
+ }
+
+ // What abou the PFILE_OBJECT?
+
+ b = GET_STRING( Buffer, Icb.NwQueryTemplate );
+ if ( b ) {
+ printf( "NwQueryTemplate : %s\n", Buffer );
+ } else {
+ printf( "NWQueryTemplate : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Icb.UQueryTemplate );
+ if ( b ) {
+ printf( "UQueryTemplate : %ws\n", Buffer );
+ } else {
+ printf( "UQueryTemplate : (unreadable)\n" );
+ }
+
+ printf( "IndexLastIcbRtr : %d\n", Icb.IndexOfLastIcbReturned );
+ printf( "Pid : %d\n", Icb.Pid );
+ printf( "DotReturned : %s\n", Icb.DotReturned ? "TRUE" : "FALSE" );
+ printf( "DotDotReturned : %s\n", Icb.DotDotReturned ? "TRUE" : "FALSE" );
+ printf( "ReturnedSmthng : %s\n", Icb.ReturnedSomething ? "TRUE" : "FALSE" );
+ printf( "ShortNameSearch : %s\n", Icb.ShortNameSearch ? "TRUE" : "FALSE" );
+ printf( "SearchHandle : %d\n", Icb.SearchHandle );
+ printf( "SearchVolume : %d\n", Icb.SearchVolume );
+ printf( "SearchAttribts : %d\n", Icb.SearchAttributes );
+ printf( "SearchIndexHigh : %d\n", Icb.SearchIndexHigh );
+ printf( "SearchIndexLow : %d\n", Icb.SearchIndexLow );
+ printf( "IsPrintJob : %s\n", Icb.IsPrintJob ? "TRUE" : "FALSE" );
+ printf( "JobId : %d\n", Icb.JobId );
+ printf( "ActuallyPrinted : %s\n", Icb.ActuallyPrinted ? "TRUE" : "FALSE" );
+ printf( "USetLastAccessTime : %s\n", Icb.UserSetLastAccessTime ? "TRUE" : "FALSE" );
+ printf( "File Position : %d\n", Icb.FilePosition );
+ printf( "File Size : %d\n", Icb.FileSize );
+
+ printf( "IsTreeHanle : %s\n", Icb.IsTreeHandle ? "TRUE" : "FALSE" );
+
+ // This needs to be cleaned up!
+
+ printf( "---------------------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpIrpContext(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+{
+ BOOL b;
+ IRP_CONTEXT IrpContext;
+
+ b = getmem( (PVOID)addr, &IrpContext, sizeof( IrpContext ), NULL );
+ if ( b == 0 ) {
+ printf( "<could not read the irpcontext>\n" );
+ return;
+ }
+
+ printf( "--------------------------IRP CONTEXT at %08lx--------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_IRP_CONTEXT\n" );
+
+ // WORK_QUEUE_ITEM?
+
+ printf( "PacketType : %s\n", PacketToString(IrpContext.PacketType));
+ printf( "NpScb Addr : %08lx\n", IrpContext.pNpScb );
+ printf( "Scb Addr : %08lx\n", IrpContext.pScb );
+ printf( "TdiStruct : %08lx\n", IrpContext.pTdiStruct );
+
+ // NextRequest?
+
+ printf( "Event : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, Event ) );
+ printf( "Original IRP : %08lx\n", IrpContext.pOriginalIrp );
+ printf( "Original SB : %08lx\n", IrpContext.pOriginalSystemBuffer );
+ printf( "Original UB : %08lx\n", IrpContext.pOriginalUserBuffer );
+ printf( "Original MDL : %08lx\n", IrpContext.pOriginalMdlAddress );
+ printf( "Receive IRP : %08lx\n", IrpContext.ReceiveIrp );
+ printf( "TxMdl : %08lx\n", IrpContext.TxMdl );
+ printf( "RxMdl : %08lx\n", IrpContext.RxMdl );
+ printf( "RunRoutine : %08lx\n", IrpContext.RunRoutine );
+ printf( "pEx : %08lx\n", IrpContext.pEx );
+ printf( "PostProcessRtn : %08lx\n", IrpContext.PostProcessRoutine );
+ printf( "TimeoutRtn : %08lx\n", IrpContext.TimeoutRoutine );
+ printf( "ComplSendRtn : %08lx\n", IrpContext.CompletionSendRoutine );
+ printf( "pWorkItem : %08lx\n", IrpContext.pWorkItem );
+ printf( "Req Data Addr : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, req ) );
+ printf( "ResponseLength : %08lx\n", IrpContext.ResponseLength );
+ printf( "Rsp Data Addr : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, rsp ) );
+ printf( "Icb Addr : %08lx\n", IrpContext.Icb );
+ printf( "Specific Data Addr : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, Specific.Create.FullPathName ) );
+ printf( "------------------------------IRP Context Flags----------------------------\n");
+ PrintIrpContextFlags(IrpContext.Flags, lpExtensionApis);
+ printf( "---------------------------------------------------------------------------\n" );
+
+ return;
+}
+
+VOID
+DumpFcbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ NONPAGED_FCB NpFcb;
+
+ b = getmem( (PVOID)addr, &NpFcb, sizeof( NONPAGED_FCB ), NULL);
+ if ( !b ) {
+ printf( "<could not read the non-pageable fcb>\n" );
+ return;
+ }
+
+ printf( "--------------------Common NP FCB Header at %08lx-----------------------\n");
+ printf( "NodeTypeCode : NW_NTC_NONPAGED_FCB\n" );
+ printf( "NodeByteSize : %d\n", NpFcb.Header.NodeByteSize );
+ printf( "IsFastIoPossible : %d\n", NpFcb.Header.IsFastIoPossible );
+
+ // Resource? PagingIoResource?
+
+ printf( "AllocationSize : %08lx %08lx\n", NpFcb.Header.AllocationSize.HighPart, NpFcb.Header.AllocationSize.LowPart );
+ printf( "FileSize : %08lx %08lx\n", NpFcb.Header.FileSize.HighPart, NpFcb.Header.FileSize.LowPart );
+ printf( "ValidDataLength : %08lx %08lx\n", NpFcb.Header.ValidDataLength.HighPart, NpFcb.Header.ValidDataLength.LowPart );
+ printf( "pFcb Addr : %08lx\n", NpFcb.Fcb );
+
+ // SegmentObject?
+
+ printf( "FileLockList : %08lx\n", addr + FIELD_OFFSET( NONPAGED_FCB, FileLockList) );
+ printf( "PendLockList : %08lx\n", addr + FIELD_OFFSET( NONPAGED_FCB, PendingLockList) );
+ printf( "Resource : %08lx\n", addr + FIELD_OFFSET( NONPAGED_FCB, Resource ) );
+
+ printf( "Attributes : %d\n", NpFcb.Attributes );
+ printf( "CacheType : %d\n", NpFcb.CacheType );
+ printf( "CacheBuffer : %08lx\n", NpFcb.CacheBuffer );
+ printf( "CacheMdl : %08lx\n", NpFcb.CacheMdl );
+ printf( "CacheSize : %d\n", NpFcb.CacheSize );
+ printf( "CacheFileOffset : %d\n", NpFcb.CacheFileOffset );
+ printf( "CacheDataSize : %d\n", NpFcb.CacheDataSize );
+ printf( "----------------------------------FCB Flags--------------------------------\n" );
+ PrintNpFcbFlags( NpFcb.Header.Flags, lpExtensionApis );
+
+ // Dump both parts.
+ if ( first )
+ DumpFcb( (DWORD)NpFcb.Fcb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpRcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of an ICB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the ICB.
+
+--*/
+{
+ BOOL b;
+ RCB Rcb;
+
+ b = getmem( (PVOID)addr, &Rcb, sizeof( RCB ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the rcb>\n");
+ return;
+ }
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_RCB\n");
+ printf( "State : %s\n", RcbStateToString(Rcb.State));
+ printf( "OpenCount : %ul\n", Rcb.OpenCount);
+ printf( "ResourceAddr : %08lx\n", addr + FIELD_OFFSET( RCB, Resource ));
+ printf( "ServerListAddr : %08lx\n", addr + FIELD_OFFSET( RCB,
+ ServerNameTable ));
+ printf( "VolumeListAddr : %08lx\n", addr + FIELD_OFFSET( RCB,
+ VolumeNameTable ));
+ printf( "FileListAddr : %08lx\n", addr + FIELD_OFFSET( RCB,
+ FileNameTable ));
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpPid(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a PID and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the PID.
+
+--*/
+{
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_PID\n" );
+ printf( "...Not yet implemented...");
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpFileLock(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a file lock and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the file lock.
+
+--*/
+{
+
+ printf( "------------------------------------------------------------\n" );
+ printf( "NodeTypeCode : NW_NTC_FILE_LOCK\n" );
+ printf( "Not yet implemented...\n" );
+ printf( "------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpLogon(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a logon and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the logon.
+
+--*/
+{
+ BOOL b;
+ LOGON Logon;
+ WCHAR Buffer[64];
+
+ b = getmem( (PVOID)addr, &Logon, sizeof(LOGON), NULL );
+ if (!b ) {
+ printf( "<unable to read logon>" );
+ return;
+ }
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_LOGON\n" );
+ printf( "NodeByteSize : %d\n", Logon.NodeByteSize );
+ printf( "NextLogon : %08lx (LOGON LIST_ENTRY)\n", addr +
+ FIELD_OFFSET( LOGON, Next ));
+
+ b = GET_STRING( Buffer, Logon.UserName );
+ if ( b ) {
+ printf( "UserName : %ws\n", Buffer );
+ } else {
+ printf( "UserName : <unreadable>\n" );
+ }
+
+ b = GET_STRING( Buffer, Logon.PassWord );
+ if ( b ) {
+ printf( "Password : %ws\n", Buffer );
+ } else {
+ printf( "Password : <unreadable>\n" );
+ }
+
+ b = GET_STRING( Buffer, Logon.ServerName );
+ if ( b ) {
+ printf( "Pref Server : %ws\n", Buffer );
+ } else {
+ printf( "Pref Server : <unreadable>\n" );
+ }
+
+ printf( "UserUid : %08lx %08lx\n", Logon.UserUid.HighPart,
+ Logon.UserUid.LowPart);
+
+ printf( "CredListResource: %08lx\n", addr +
+ FIELD_OFFSET( LOGON, CredentialListResource ));
+
+ printf( "CredentialList : %08lx (CREDENTIAL LIST_ENTRY)\n", addr +
+ FIELD_OFFSET( LOGON, NdsCredentialList ));
+
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpCredential(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of an nds credential and a
+ pointer to a debugger extension interface block. It prints
+ out the information in the logon.
+
+--*/
+{
+ BOOL b;
+ NDS_SECURITY_CONTEXT Context;
+ NDS_CREDENTIAL Credential;
+ NDS_SIGNATURE Signature;
+
+ WCHAR Buffer[512];
+
+ CHAR PackBuffer[2048];
+ BYTE *packed;
+ ULONG packedlen;
+
+ b = getmem( (PVOID)addr, &Context, sizeof(NDS_SECURITY_CONTEXT), NULL );
+ if (!b ) {
+ printf( "<unable to read context>\n" );
+ return;
+ }
+
+ printf( "-------- NDS Security Context at 0x%08lx ----------------\n", addr);
+ printf( "NodeTypeCode : NW_NTC_NDS_CREDENTIAL\n" );
+ printf( "NodeByteSize : %d\n", Context.nts );
+
+ printf( "Next : %08lx (NDS_SECURITY_CONTEXT LIST_ENTRY)\n", addr +
+ FIELD_OFFSET( NDS_SECURITY_CONTEXT, Next ));
+
+
+ b = GET_STRING( Buffer, Context.NdsTreeName );
+ if ( b ) {
+ printf( "Nds Tree Name : %ws\n", Buffer );
+ } else {
+ printf( "Nds Tree Name : <unreadable>\n" );
+ }
+
+ b = GET_STRING( Buffer, Context.CurrentContext );
+ if ( b ) {
+ printf( "Current Context : %ws\n", Buffer );
+ } else {
+ printf( "Current Context :<unreadable>\n" );
+ }
+
+ if ( Context.Credential != NULL ) {
+
+ printf( "--------------------- Credential Data ----------------------\n");
+
+ b = getmem( (PVOID)Context.Credential, &Credential, sizeof(NDS_CREDENTIAL), NULL );
+ if (!b ) {
+ printf( "<unable to read credential>\n" );
+ goto DO_SIGNATURE;
+ }
+
+ printf( "Start validity : 0x%08lx\n", Credential.validityBegin );
+ printf( "End validity : 0x%08lx\n", Credential.validityEnd );
+ printf( "Random : 0x%08lx\n", Credential.random );
+ printf( "Opt data Len : %d\n", Credential.optDataSize );
+ printf( "UserName Len : %d\n", Credential.userNameLength );
+
+ //
+ // Optional data is the first packed data after the struct.
+ //
+
+ packedlen = Credential.optDataSize + Credential.userNameLength;
+ packed = ((BYTE *)Context.Credential) + sizeof( NDS_CREDENTIAL );
+
+ if ( Credential.optDataSize ) {
+ printf( "Opt data addr : %08lx\n", packed );
+ }
+
+ packed += Credential.optDataSize;
+
+ b = getmem( (PVOID)packed, Buffer, Credential.userNameLength, NULL );
+ if ( !b ) {
+ printf( "<unable to read user name>\n" );
+ goto DO_SIGNATURE;
+ }
+ printf( "Username : %ws\n", Buffer );
+
+ } else {
+
+ printf( "-------------------- No Credential Data --------------------\n");
+
+ }
+
+DO_SIGNATURE:
+
+ if ( Context.Signature != NULL ) {
+
+ printf( "---------------------- Signature Data ----------------------\n");
+
+ b = getmem( (PVOID)Context.Signature, &Signature, sizeof(NDS_SIGNATURE), NULL );
+ if (!b ) {
+ printf( "<unable to read signature>\n" );
+ goto DO_END;
+ }
+
+ printf( "Signature Len : %d\n", Signature.signDataLength );
+
+ packedlen = Signature.signDataLength;
+ packed = ((BYTE *)Context.Signature) + sizeof( NDS_SIGNATURE );
+
+ printf( "Signature addr : %08lx\n", packed );
+
+ } else {
+
+ printf( "-------------------- No Signature Data ---------------------\n");
+
+ }
+
+DO_END:
+
+ if ( Context.PublicNdsKey != NULL ) {
+
+ printf( "------------------------------------------------------------\n");
+
+ printf( "Public Key Len : %d\n", Context.PublicKeyLen );
+ printf( "Public Key : %08lx\n", Context.PublicNdsKey );
+
+ printf( "------------------------------------------------------------\n");
+
+ } else {
+
+ printf( "-------------------- No Public Key Data --------------------\n");
+
+ }
+
+}
+
+
+VOID
+DumpMiniIrpContext(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a mini irp context
+ and a pointer to a debugger extension interface block.
+ It prints out the information in the mini irp context.
+
+--*/
+{
+ BOOL b;
+ MINI_IRP_CONTEXT mini;
+
+ b = getmem( (PVOID)addr, &mini, sizeof(MINI_IRP_CONTEXT), NULL );
+ if (!b ) {
+ printf( "<unable to read mini irp context>\n");
+ return;
+ }
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_MINI_IRP_CONTEXT\n" );
+ printf( "NodeByteSize : %d\n", mini.NodeByteSize );
+ printf( "ListEntry : %08lx\n", addr + FIELD_OFFSET( MINI_IRP_CONTEXT,
+ Next ));
+ printf( "IrpContext : %08lx\n", mini.IrpContext );
+ printf( "Irp : %08lx\n", mini.Irp );
+ printf( "Buffer : %08lx\n", mini.Buffer );
+ printf( "Mdl1 : %08lx\n", mini.Mdl1 );
+ printf( "Mdl2 : %08lx\n", mini.Mdl2 );
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+nwdump(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function takes the pointer to a structure,
+ figures out what the structure is, and calls the
+ appropriate dump routine.
+
+Arguments:
+
+ CurrentPc - Supplies the current pc at the time
+ the extension is called.
+
+ lpExtensionApis - Supplies the address of the
+ functions callable by this extension.
+
+ lpArgumentString - Supplies the address of the structure.
+
+Return Value:
+
+ None.
+
+---*/
+{
+
+ DWORD addr;
+
+ //
+ // Determine the node type and dispatch.
+ //
+
+ addr = getexpr( lpArgumentString );
+
+ switch ( GetNodeType( addr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ DumpScb(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_SCBNP:
+
+ DumpScbNp(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_FCB:
+ case NW_NTC_DCB:
+
+ DumpFcb(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_VCB:
+
+ DumpVcb(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_ICB:
+ case NW_NTC_ICB_SCB:
+
+ DumpIcb(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_IRP_CONTEXT:
+
+ DumpIrpContext(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_NONPAGED_FCB:
+
+ DumpFcbNp(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_RCB:
+
+ DumpRcb(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_PID:
+
+ DumpPid(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_FILE_LOCK:
+
+ DumpFileLock(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_LOGON:
+
+ DumpLogon(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_MINI_IRP_CONTEXT:
+
+ DumpMiniIrpContext(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_NDS_CREDENTIAL:
+
+ DumpCredential(addr, lpExtensionApis);
+ break;
+
+ default:
+
+ printf("(this object does not have a vaid node type)\n");
+ break;
+ }
+
+}
+
+//
+// Other debugger routines.
+//
+
+VOID
+serverlist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function displays a list of servers that the redirector
+ is maintaining connections to. The information is read from
+ the SCB queue, not from the server list in the RCB. The
+ argument to this function is ignored.
+
+--*/
+{
+
+ DWORD addrScbQueue;
+ WCHAR ServerName[64];
+ BOOL b;
+ PLIST_ENTRY ScbQueueList;
+ DWORD addrNpScb, addrScb;
+ NONPAGED_SCB NpScb;
+ SCB Scb;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ //
+ // Get the address of the server list in the rdr.
+ //
+
+ addrScbQueue = getsymaddr("nwrdr!scbqueue");
+
+ if ( addrScbQueue == 0 ) {
+ printf("The server list was not locatable.\n");
+ return;
+ }
+
+ //
+ // Walk the list of servers.
+ //
+
+ printf("pNpScb pScb Ref State Name\n");
+ printf("---------------------------------------------------------------------------\n");
+
+ for ( GET_DWORD( &ScbQueueList, addrScbQueue );
+ ScbQueueList != (PLIST_ENTRY)addrScbQueue;
+ GET_DWORD( &ScbQueueList, ScbQueueList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrNpScb = (DWORD)CONTAINING_RECORD( ScbQueueList, NONPAGED_SCB, ScbLinks );
+
+ printf("%08lx ", addrNpScb );
+
+ b = (getmem)((LPVOID)addrNpScb,
+ &NpScb,
+ sizeof( NpScb ),
+ NULL);
+
+ if ( b == 0 ) {
+ printf("<could not continue>\n");
+ return;
+ }
+
+ addrScb = (DWORD)NpScb.pScb;
+ printf("%08lx ", addrScb );
+
+ printf("%8lx ", NpScb.Reference);
+ printf("%-25s", ScbStateToString( NpScb.State ) );
+
+ if ( addrScb != 0 ) {
+ b = (getmem)((LPVOID)addrScb,
+ &Scb,
+ sizeof( Scb ),
+ NULL);
+
+ if ( b == 0 ) {
+ printf("<unreadable>\n");
+ continue;
+ }
+
+ // Get the server name.
+
+ b = GET_STRING( ServerName, Scb.UidServerName );
+
+ if ( b ) {
+ printf( "%ws\n", ServerName );
+ } else {
+ printf( "Unreadable\n" );
+ }
+ } else {
+ printf( "Permanent SCB\n" );
+ }
+
+ }
+
+ printf("---------------------------------------------------------------------------\n");
+
+}
+
+VOID
+trace(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the nwrdr trace buffer. Arguments to
+ this function are ignored.
+
+To Be Done:
+
+ Read trace buffer size out of nwrdrd and dynamically size.
+
+--*/
+
+{
+ ULONG addrDBuffer, addrDBufferPtr, DBufferPtr;
+ ULONG BufferSize;
+ PCHAR TraceStart, CurrentPtr;
+ char buffer[80 + 1];
+ char *bptr;
+ char *newptr;
+ int i;
+ int readsize;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ addrDBuffer = getsymaddr( "nwrdr!dbuffer" );
+
+ if ( !addrDBuffer ) {
+ printf("(unable to locate the trace buffer address)\n");
+ return;
+ } else {
+ printf("Address of Dbuffer = %08lx\n", addrDBuffer );
+ }
+
+ addrDBufferPtr = getsymaddr( "nwrdr!dbufferptr" );
+
+ if ( !addrDBuffer ) {
+ printf("(unable to locate the trace buffer pointer)\n");
+ return;
+ } else {
+ printf("Address of DbufferPtr = %08lx\n", addrDBufferPtr );
+ }
+
+ GET_DWORD( &DBufferPtr, addrDBufferPtr );
+ printf("DbufferPtr = %08lx\n", DBufferPtr );
+
+ // Set up state variables and loop.
+
+ TraceStart = (char *)addrDBuffer;
+ BufferSize = 100*255+1;
+ CurrentPtr = (char *)DBufferPtr;
+
+ buffer[80] = '\0';
+ newptr = CurrentPtr + 1;
+ while ( 1 ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ if ( newptr + 80 > TraceStart+BufferSize ) {
+ readsize = TraceStart+BufferSize - newptr;
+ } else {
+ readsize = 80;
+ }
+
+ getmem( newptr, buffer, readsize, NULL );
+
+ bptr = buffer;
+ for (i = 0; i<80 ; i++ ) {
+ if ( buffer[i] == '\n') {
+ buffer[i] = 0;
+ printf( "%s\n", bptr );
+ bptr = &buffer[i+1];
+ }
+ }
+ printf( "%s", bptr );
+
+ //
+ // If we're back to where we started, break out of here.
+ //
+
+ if ( (newptr <= CurrentPtr) &&
+ (newptr + readsize) >= CurrentPtr ) {
+ break;
+ }
+
+ //
+ // Advance the running pointer.
+ //
+
+ newptr += readsize;
+ if ( newptr >= TraceStart+BufferSize ) {
+ newptr = TraceStart;
+ }
+ }
+ printf( "\n");
+}
+
+VOID
+reftrace(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the nwrdr reference trace buffer.
+
+--*/
+{
+ ULONG addrRBuffer, addrRBufferPtr, RBufferPtr;
+ ULONG BufferSize;
+ PCHAR TraceStart, CurrentPtr;
+ char buffer[80 + 1];
+ char *bptr;
+ char *newptr;
+ int i;
+ int readsize;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ addrRBuffer = getsymaddr( "nwrdr!RBuffer" );
+
+ if ( !addrRBuffer ) {
+ printf("(unable to locate the trace buffer address)\n");
+ return;
+ } else {
+ printf("Address of RBuffer = %08lx\n", addrRBuffer );
+ }
+
+ addrRBufferPtr = getsymaddr( "nwrdr!RBufferptr" );
+
+ if ( !addrRBuffer ) {
+ printf("(unable to locate the trace buffer pointer)\n");
+ return;
+ } else {
+ printf("Address of RBufferPtr = %08lx\n", addrRBufferPtr );
+ }
+
+ GET_DWORD( &RBufferPtr, addrRBufferPtr );
+ printf("RBufferPtr = %08lx\n", RBufferPtr );
+
+ // Set up state variables and loop.
+
+ TraceStart = (char *)addrRBuffer;
+ BufferSize = 100*255+1;
+ CurrentPtr = (char *)RBufferPtr;
+
+ buffer[80] = '\0';
+ newptr = CurrentPtr + 1;
+ while ( 1 ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ if ( newptr + 80 > TraceStart+BufferSize ) {
+ readsize = TraceStart+BufferSize - newptr;
+ } else {
+ readsize = 80;
+ }
+
+ getmem( newptr, buffer, readsize, NULL );
+
+ bptr = buffer;
+ for (i = 0; i<80 ; i++ ) {
+ if ( buffer[i] == '\n') {
+ buffer[i] = 0;
+ printf( "%s\n", bptr );
+ bptr = &buffer[i+1];
+ }
+ }
+ printf( "%s", bptr );
+
+ //
+ // If we're back to where we started, break out of here.
+ //
+
+ if ( (newptr <= CurrentPtr) &&
+ (newptr + readsize) >= CurrentPtr ) {
+ break;
+ }
+
+ //
+ // Advance the running pointer.
+ //
+
+ newptr += readsize;
+ if ( newptr >= TraceStart+BufferSize ) {
+ newptr = TraceStart;
+ }
+ }
+ printf( "\n");
+}
+
+VOID
+logonlist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This routine prints out the logon list for the rdr. Arguments
+ to this function are ignored.
+
+--*/
+
+{
+ DWORD addrLogonList;
+ WCHAR Data[64];
+ BOOL b;
+ PLIST_ENTRY LogonList;
+ DWORD addrLogonEntry;
+ LOGON Logon;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Get the address of the logon list.
+
+ addrLogonList = getsymaddr( "nwrdr!logonlist" );
+
+ if ( addrLogonList == 0 ) {
+ printf("The logon list could not be located.\n");
+ return;
+ }
+
+ // Walk the list of servers
+
+ printf("pLogon User Name Password Pref Server UID\n" );
+ printf("---------------------------------------------------------------------------\n" );
+
+ for ( GET_DWORD( &LogonList, addrLogonList );
+ LogonList != (PLIST_ENTRY)addrLogonList;
+ GET_DWORD( &LogonList, LogonList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrLogonEntry = (DWORD)CONTAINING_RECORD( LogonList, LOGON, Next );
+
+ printf("%08lx ", addrLogonEntry );
+
+ b = (getmem)((LPVOID)addrLogonEntry,
+ &Logon,
+ sizeof( Logon ),
+ NULL);
+
+ if ( b == 0 ) return;
+
+ if ( Logon.NodeTypeCode != NW_NTC_LOGON ) {
+ printf( "<invalid node type>\n" );
+ return;
+ }
+
+ b = GET_STRING( Data, Logon.UserName );
+
+ if ( b ) {
+ printf( "%-15ws", Data );
+ } else {
+ printf( "%-15s", "Unreadable" );
+ }
+
+ /*
+ b = GET_STRING( Data, Logon.PassWord );
+
+ if ( b ) {
+ printf( "%-15ws", Data );
+ } else {
+ printf( "%-15s", "Unreadable" );
+ }
+ */
+ printf( "%-15s", "<secret>" );
+
+ b = GET_STRING( Data, Logon.ServerName );
+
+ if ( b ) {
+ printf( "%-15ws", Data );
+ } else {
+ printf( "%-15s", "Unreadable" );
+ }
+
+ printf( "%08lx:%08x\n", Logon.UserUid.HighPart, Logon.UserUid.LowPart );
+ }
+
+ printf("---------------------------------------------------------------------------\n" );
+
+}
+
+//
+// Functions that help mangle lists of objects.
+//
+
+VOID
+vcblist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to the pageable portion
+ or non-pageable portion of an SCB and dumps the VCB
+ list for that SCB.
+
+--*/
+{
+ BOOL b;
+ PVOID objAddr;
+
+ PLIST_ENTRY VcbList;
+ DWORD addrVcbList;
+ PVCB addrVcb;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ // Invariant: If we leave the switch, objAddr must point to the
+ // pageable portion of the SCB that we are interested in.
+
+ switch ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ break;
+
+ case NW_NTC_SCBNP:
+
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( NONPAGED_SCB, pScb ) ) );
+ if ( objAddr == 0 ) return;
+ break;
+
+ default:
+
+ printf( "(invalid node type code: argument must point to an scb or npscb)\n" );
+ return;
+ }
+
+ // Get the head of the vcb list.
+ addrVcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( SCB, ScbSpecificVcbQueue ));
+
+ // Walk the list and print.
+ for ( GET_DWORD( &VcbList, addrVcbList ) ;
+ VcbList != (PLIST_ENTRY)addrVcbList ;
+ GET_DWORD( &VcbList, VcbList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrVcb = (PVCB)CONTAINING_RECORD( VcbList, VCB, VcbListEntry );
+ if( GetNodeType( (DWORD)addrVcb, lpExtensionApis ) != NW_NTC_VCB )
+ printf( "(invalid entry in vcb list)\n" );
+ else
+ DumpVcb( (DWORD)addrVcb, lpExtensionApis );
+ }
+}
+
+VOID
+irplist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to the non-pageable portion
+ of an SCB and dumps the IRP list for that non-pageable SCB.
+
+--*/
+{
+ PLIST_ENTRY IrpList;
+ DWORD addrIrpList;
+ PIRP_CONTEXT addrIrp;
+
+ PVOID objAddr;
+ BOOL b;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ // Invariant: If we leave the switch, objAddr must point to the
+ // non-pageable portion of the SCB that we are interested in.
+
+ switch ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( SCB, pNpScb ) ) );
+ if ( objAddr == 0 ) return;
+ break;
+
+ case NW_NTC_SCBNP:
+
+ break;
+
+ default:
+
+ printf( "(invalid node type code: argument must point to an scb or npscb)\n" );
+ return;
+ }
+
+ // Get the head of the request list.
+ addrIrpList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( NONPAGED_SCB, Requests ));
+
+ // Walk the list and print.
+ for ( GET_DWORD( &IrpList, addrIrpList ) ;
+ IrpList != (PLIST_ENTRY)addrIrpList ;
+ GET_DWORD( &IrpList, IrpList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrIrp = (PIRP_CONTEXT)CONTAINING_RECORD( IrpList, IRP_CONTEXT, NextRequest );
+ if( GetNodeType( (DWORD)addrIrp, lpExtensionApis ) != NW_NTC_IRP_CONTEXT )
+ printf( "(invalid entry in the irp context list)\n" );
+ else
+ DumpIrpContext( (DWORD)addrIrp, lpExtensionApis );
+ }
+}
+
+VOID
+fcblist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to a VCB and dumps
+ the FCB list for that VCB.
+
+--*/
+{
+ PLIST_ENTRY FcbList;
+ DWORD addrFcbList;
+ PFCB addrFcb;
+
+ NODE_TYPE_CODE ntc;
+ PVOID objAddr;
+ BOOL b;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ if ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) != NW_NTC_VCB ) {
+
+ printf( "(invalid node type code: argument must point to a vcb)\n" );
+ return;
+ }
+
+ // Get the head of the fcb list.
+ addrFcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( VCB, FcbList ));
+
+ for ( GET_DWORD( &FcbList, addrFcbList ) ;
+ FcbList != (PLIST_ENTRY)addrFcbList ;
+ GET_DWORD( &FcbList, FcbList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrFcb = (PFCB)CONTAINING_RECORD( FcbList, FCB, FcbListEntry );
+ ntc = GetNodeType( (DWORD)addrFcb, lpExtensionApis );
+ if( (ntc != NW_NTC_FCB) && (ntc != NW_NTC_DCB) )
+ printf( "(invalid entry in the fcb list)\n" );
+ else
+ DumpFcb( (DWORD)addrFcb, lpExtensionApis, TRUE );
+ }
+
+}
+
+VOID
+icblist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to the pageable portion
+ of an SCB or FCB and dumps the ICB list for that SCB or FCB.
+
+--*/
+{
+ PVOID objAddr;
+ BOOL b;
+ NODE_TYPE_CODE ntc;
+
+ PICB addrIcb;
+ PLIST_ENTRY IcbList;
+ DWORD addrIcbList, IcbCount;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ // Invariant: If we leave the switch, addrIcbList must point
+ // to the head of the ICB list that we are interested in.
+
+ switch ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( SCB, IcbList ));
+ break;
+
+ case NW_NTC_SCBNP:
+
+ // Look up the pageable portion.
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( NONPAGED_SCB, pScb ) ) );
+ if ( objAddr == 0 ) return;
+ // Now get it.
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( SCB, IcbList));
+ break;
+
+ case NW_NTC_FCB:
+ case NW_NTC_DCB:
+
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( FCB, IcbList ));
+ break;
+
+ case NW_NTC_NONPAGED_FCB:
+
+ // Look up the pageable portion.
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( NONPAGED_FCB, Fcb ) ) );
+ if (objAddr == 0) return;
+ // Now get it.
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( FCB, IcbList ));
+ break;
+
+ default:
+
+ printf( "(invalid node type: argument must be: scb, npscb, fcb, dcb, or npfcb)\n" );
+ return;
+ }
+
+ // Walk the list.
+ for ( GET_DWORD( &IcbList, addrIcbList ) ;
+ IcbList != (PLIST_ENTRY)addrIcbList ;
+ GET_DWORD( &IcbList, IcbList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrIcb = (PICB)CONTAINING_RECORD( IcbList, ICB, ListEntry );
+ ntc = GetNodeType( (DWORD)addrIcb, lpExtensionApis );
+ if( (ntc != NW_NTC_ICB) && (ntc != NW_NTC_ICB_SCB) )
+ printf( "(invalid entry in icb list)\n" );
+ else
+ DumpIcb( (DWORD)addrIcb, lpExtensionApis );
+
+ }
+
+}
+
+VOID
+credlist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to a LOGON and dumps
+ the NDS credential list for that user.
+
+--*/
+{
+ PLIST_ENTRY CredList;
+ DWORD addrCredList;
+ PNDS_SECURITY_CONTEXT addrCred;
+
+ NODE_TYPE_CODE ntc;
+ PVOID objAddr;
+ BOOL b;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ if ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) != NW_NTC_LOGON ) {
+
+ printf( "(invalid node type code: argument must point to a logon)\n" );
+ return;
+ }
+
+ // Get the head of the fcb list.
+ addrCredList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( LOGON, NdsCredentialList ));
+
+ for ( GET_DWORD( &CredList, addrCredList ) ;
+ CredList != (PLIST_ENTRY)addrCredList ;
+ GET_DWORD( &CredList, CredList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrCred = (PNDS_SECURITY_CONTEXT)
+ CONTAINING_RECORD( CredList,
+ NDS_SECURITY_CONTEXT,
+ Next );
+ ntc = GetNodeType( (DWORD)addrCred, lpExtensionApis );
+ if( (ntc != NW_NTC_NDS_CREDENTIAL ) )
+ printf( "(invalid entry in the credential list)\n" );
+ else
+ DumpCredential( (DWORD)addrCred, lpExtensionApis);
+ printf("\n");
+ }
+
+}
diff --git a/private/nw/rdr/kdext/windbg/makefile b/private/nw/rdr/kdext/windbg/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/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/rdr/kdext/windbg/nwdbg.def b/private/nw/rdr/kdext/windbg/nwdbg.def
new file mode 100644
index 000000000..ca9758039
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/nwdbg.def
@@ -0,0 +1,15 @@
+DESCRIPTION 'NT Netware Redirector KD extensions'
+
+EXPORTS
+ nwdump
+ logonlist
+ serverlist
+ trace
+ traceflags
+ help
+ vcblist
+ fcblist
+ icblist
+ irplist
+ credlist
+ reftrace
diff --git a/private/nw/rdr/kdext/windbg/nwdbg.rc b/private/nw/rdr/kdext/windbg/nwdbg.rc
new file mode 100644
index 000000000..5d9a5fe78
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/nwdbg.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 WinDbg Debugger Extensions DLL"
+#define VER_INTERNALNAME_STR "NwDbg.DLL"
+#define VER_ORIGINALFILENAME_STR "NwDbg.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/rdr/kdext/windbg/sources b/private/nw/rdr/kdext/windbg/sources
new file mode 100644
index 000000000..7f3d51824
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/sources
@@ -0,0 +1,51 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=rdr
+
+TARGETNAME=nwdbg
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib
+
+
+DLLBASE=0x1010000
+
+INCLUDES=..\..;..\..\..\inc;$(NTOS_ROOT)\inc;$(_NTROOT)\private\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+C_DEFINES=-DWINDBG
+
+SOURCES=..\nwrdrkd.c \
+ nwdbg.rc
+
+UMTYPE=console
+OPTIONAL_NTTEST=
+
diff --git a/private/nw/rdr/lock.c b/private/nw/rdr/lock.c
new file mode 100644
index 000000000..42d069f5e
--- /dev/null
+++ b/private/nw/rdr/lock.c
@@ -0,0 +1,1357 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Lock.c
+
+Abstract:
+
+ This module implements the Lock routine for the NetWare redirector.
+
+ Notes on the implementation of locks.
+
+ o Netware servers handle lock conflicts differently than a LAN Man
+ server, or NT file system would. In particular:
+
+ - A lock conflict on a single file handle (i.e. the same app owns
+ the lock, and is trying to obtain a conflicting lock): The
+ netware server will fail the request only if the lock range is
+ identical to a held lock. Also, the lock fails immediately, even
+ if the app requested a blocking lock.
+
+ - A lock conflict generated by 2 app from the same workstation:
+ The server will fail the request if the request lock overlaps an
+ existing lock by even a single byte, but the server will fail the
+ request immediately, even if the app requested a blocking lock.
+
+ - A lock conflict generated by 2 different workstations: This works
+ as expected. The lock fails if it overlaps an existing lock, and
+ the request blocks if requested by the app.
+
+ o The NT workstation needs to impose NT file system behaviour when dealing
+ with a netware server. There are 2 key elements (complications)
+ added to the redirector to handle this.
+
+ - A locally maintained lock database. This is used to test for
+ lock conflicts locally. If a conflict is detected and the
+ requestor asks for a blocking lock, the lock request is queued
+ to a local lock conflict list. This list is processed when real
+ locks are released.
+
+ - A pending lock list. This is used to poll the netware server
+ about remote lock conflicts. We could not let our lock request
+ block indefinitely as this would tie up our one channel of
+ communication to the server.
+
+ o The data structures
+
+ - NonPagedFcb
+ -> FileLockList - The list of existing locks.
+ -> PendingLockList - The list of locks pending due to a
+ local conflict.
+
+ - NwPendingLockList
+ The list of locks pending due to a remote conflict. The
+ locks are retried indefinitely using a polling mechanism.
+
+ A request can be removed from the pending list via (1) a
+ cleanup for the correct ICB (2) the IRP can be cancelled.
+ (3) The server actually grants the lock.
+
+ o Other notes:
+
+ We play some games to allow us to use the FCB resource as the
+ synchronization mechanism, even though much processing happens
+ at raised IRQL. Be careful not to break this.
+
+Author:
+
+ Colin Watson [ColinW] 13-May-1993
+ Manny Weiser [MannyW] 16-May-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_LOCKCTRL)
+
+NTSTATUS
+NwCommonLock(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+LockNcp(
+ PIRP_CONTEXT IrpContext,
+ PICB Icb
+ );
+
+NTSTATUS
+LockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+UnlockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+BOOLEAN
+LockIsOverlapping(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartFileOffset,
+ ULONG Length
+ );
+
+VOID
+AddLockToFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ );
+
+VOID
+RemoveLockFromFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ );
+
+VOID
+ReattemptPendingLocks(
+ PNONPAGED_FCB pNpFcb
+ );
+
+BOOLEAN
+LockExists(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartOffset,
+ ULONG Length,
+ PNW_FILE_LOCK *FileLock
+ );
+
+NTSTATUS
+UnlockIcbLocks(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+UnlockIcbLocksCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdLockControl )
+#pragma alloc_text( PAGE, NwCommonLock )
+#pragma alloc_text( PAGE, LockNcp )
+#pragma alloc_text( PAGE, LockIsOverlapping )
+#pragma alloc_text( PAGE, NwFreeLocksForIcb )
+#pragma alloc_text( PAGE, UnlockIcbLocks )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, LockNcpCallback )
+#pragma alloc_text( PAGE1, UnlockNcpCallback )
+#pragma alloc_text( PAGE1, AddLockToFcb )
+#pragma alloc_text( PAGE1, RemoveLockFromFcb )
+#pragma alloc_text( PAGE1, ReattemptPendingLocks )
+#pragma alloc_text( PAGE1, LockExists )
+#pragma alloc_text( PAGE1, UnlockIcbLocksCallback )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+NTSTATUS
+NwFsdLockControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtCreateFile and NtOpenFile
+ API calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the redirector.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ TimerStart(Dbg);
+ DebugTrace(+1, Dbg, "NwFsdLockControl\n", 0);
+
+ //
+ // Call the common lock routine, with block allowed if the operation
+ // is synchronous.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ Status = NwCommonLock( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdLock -> %08lx\n", Status );
+
+ TimerStop(Dbg,"NwFsdLockControl");
+
+ return Status;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+}
+
+
+NTSTATUS
+NwCommonLock (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtLockFile/NtUnlockFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonLock...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ if (nodeTypeCode != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+
+ if (nodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status );
+ return status;
+ }
+
+ switch (irpSp->MinorFunction) {
+
+ case IRP_MN_LOCK:
+ case IRP_MN_UNLOCK_SINGLE:
+ case IRP_MN_UNLOCK_ALL:
+ case IRP_MN_UNLOCK_ALL_BY_KEY:
+ status = LockNcp( IrpContext, icb );
+ break;
+
+ default:
+ //
+ // Minor function added to I/O system that this driver does
+ // not understand.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status);
+
+ return status;
+}
+
+NTSTATUS
+LockNcp(
+ PIRP_CONTEXT IrpContext,
+ PICB Icb
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of Lock NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ LARGE_INTEGER ByteOffset;
+ LARGE_INTEGER Length;
+ ULONG Key;
+
+ PSCB pScb;
+ PNONPAGED_FCB pNpFcb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ PNW_FILE_LOCK FileLock = NULL;
+ USHORT LockFlags = 3; // BUGBUG
+
+ PAGED_CODE();
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ ByteOffset = irpSp->Parameters.LockControl.ByteOffset;
+
+ if ( irpSp->Parameters.LockControl.Length != NULL ) {
+ Length = *irpSp->Parameters.LockControl.Length;
+ } else {
+ Length.HighPart = 0;
+ Length.LowPart = 0;
+ }
+
+ Key = irpSp->Parameters.LockControl.Key;
+
+ DebugTrace(+1, Dbg, "LockNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "MinorFun= %08lx\n", (ULONG)irpSp->MinorFunction);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+ DebugTrace( 0, Dbg, "HLength = %lx\n", Length.HighPart);
+ DebugTrace( 0, Dbg, "LLength = %lx\n", Length.LowPart);
+ DebugTrace( 0, Dbg, "Key = %lx\n", Key);
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ pNpFcb = Icb->SuperType.Fcb->NonPagedFcb;
+
+ //
+ // Get to the front of the ScbQueue to protect access to the lock list.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ try {
+
+ switch ( irpSp->MinorFunction ) {
+
+ case IRP_MN_LOCK:
+
+ //
+ // Since we are doing a lock we will need to send an End Of Job
+ // for this PID.
+ //
+
+ NwSetEndOfJobRequired( Icb->Pid );
+
+ //
+ // Try to allocate a lock structure before we ask the
+ // server to perform the lock.
+ //
+
+ FileLock = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NW_FILE_LOCK ) );
+ IrpContext->Specific.Lock.FileLock = FileLock;
+
+ FileLock->NodeTypeCode = NW_NTC_FILE_LOCK;
+ FileLock->NodeByteSize = sizeof( NW_FILE_LOCK );
+
+ FileLock->StartFileOffset = ByteOffset.LowPart;
+ FileLock->Length = Length.LowPart;
+ FileLock->EndFileOffset = ByteOffset.LowPart + Length.LowPart - 1;
+ FileLock->Key = Key;
+ FileLock->Icb = Icb;
+ FileLock->IrpContext = IrpContext;
+
+ if ( irpSp->Flags & SL_EXCLUSIVE_LOCK ) {
+ LockFlags = 0x00; // BUGBUG
+ } else {
+ LockFlags = 0x02; // BUGBUG
+ }
+
+ FileLock->Flags = LockFlags;
+
+ //
+ // Is this is an overlapping lock
+ //
+
+ if ( irpSp->Flags & SL_FAIL_IMMEDIATELY ) {
+ IrpContext->Specific.Lock.Wait = FALSE;
+ } else {
+ IrpContext->Specific.Lock.Wait = TRUE;
+ }
+
+ if ( LockIsOverlapping( pNpFcb, ByteOffset.LowPart, Length.LowPart ) ) {
+
+ if ( IrpContext->Specific.Lock.Wait ) {
+
+ //
+ // Queue this IRP context to the FCB. We'll process it
+ // when the local conflict is removed.
+ //
+
+ InsertTailList( &pNpFcb->PendingLockList, &FileLock->ListEntry );
+ status = STATUS_PENDING;
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ } else {
+ status = STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ } else {
+
+ //
+ // Send the lock request.
+ //
+
+ status = Exchange (
+ IrpContext,
+ LockNcpCallback,
+ "Fbrddw",
+ NCP_LOCK_RANGE,
+ LockFlags | 0x01, // BUGBUG
+ Icb->Handle, sizeof( Icb->Handle ),
+ ByteOffset.LowPart,
+ Length.LowPart,
+ LockTimeoutThreshold );
+
+ if ( !NT_SUCCESS( status ) ) {
+ FREE_POOL( FileLock );
+ }
+ }
+
+ break;
+
+ case IRP_MN_UNLOCK_SINGLE:
+
+ if ( !LockExists( pNpFcb, ByteOffset.LowPart, Length.LowPart, &FileLock ) ) {
+
+ status = STATUS_RANGE_NOT_LOCKED;
+
+ } else {
+ IrpContext->Specific.Lock.FileLock = FileLock;
+
+ status = Exchange (
+ IrpContext,
+ UnlockNcpCallback,
+ "F-rddw",
+ NCP_UNLOCK_RANGE,
+ Icb->Handle, sizeof( Icb->Handle ),
+ ByteOffset.LowPart,
+ Length.LowPart,
+ 1 );
+ }
+
+ break;
+
+ case IRP_MN_UNLOCK_ALL:
+ IrpContext->Icb = Icb;
+ IrpContext->Specific.Lock.ByKey = FALSE ;
+ IrpContext->Specific.Lock.LastLock = &pNpFcb->FileLockList;
+
+ status = UnlockIcbLocks( IrpContext );
+ break;
+
+ case IRP_MN_UNLOCK_ALL_BY_KEY:
+ IrpContext->Icb = Icb;
+ IrpContext->Specific.Lock.Key = Key ;
+ IrpContext->Specific.Lock.ByKey = TRUE ;
+ IrpContext->Specific.Lock.LastLock = &pNpFcb->FileLockList;
+
+ status = UnlockIcbLocks( IrpContext );
+ break;
+ }
+
+ } finally {
+ if ( AbnormalTermination() || !NT_SUCCESS( status ) ) {
+ if ( FileLock != NULL ) {
+ FREE_POOL( FileLock );
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+ }
+
+ DebugTrace(-1, Dbg, "LockNcb -> %08lx\n", status );
+ return status;
+}
+
+
+
+NTSTATUS
+LockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ DebugTrace(+1, Dbg, "LockNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ FREE_POOL( IrpContext->Specific.Lock.FileLock );
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if (NT_SUCCESS(Status) ) {
+
+ DebugTrace(0, Dbg, "Lock successfully applied\n", 0);
+
+ //
+ // Record this lock in the Icb lock chain
+ //
+
+ AddLockToFcb(
+ IrpContext->Icb->NpFcb,
+ IrpContext->Specific.Lock.FileLock );
+
+ } else if ( Status == STATUS_FILE_LOCK_CONFLICT &&
+ IrpContext->Specific.Lock.Wait ) {
+
+ DebugTrace(0, Dbg, "Lock conflict, adding %08lx to Pending Lock list\n", IrpContext );
+
+ //
+ // The lock conflicts with an existing lock, but the app wants
+ // to wait. Queue the request to the pending lock list and
+ // return, pending.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ IrpContext->Specific.Lock.Key = 5; // BUGBUG Configurable
+
+ ExInterlockedInsertTailList(
+ &NwPendingLockList,
+ &IrpContext->NextRequest,
+ &NwPendingLockSpinLock );
+
+ Status = STATUS_PENDING;
+
+ DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", Status);
+ return( Status );
+
+ } else {
+
+ //
+ // Status unsuccesful is returned when trying to lock 0 bytes.
+ // Map the error.
+ //
+
+ if ( Status == STATUS_UNSUCCESSFUL ) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ FREE_POOL( IrpContext->Specific.Lock.FileLock );
+ }
+
+ //
+ // If any locks were pending due to a local lock conflict, try
+ // them now.
+ //
+
+ ReattemptPendingLocks(IrpContext->Icb->NpFcb);
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", Status);
+ return Status;
+
+}
+
+
+NTSTATUS
+UnlockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ DebugTrace(0, Dbg, "UnlockNcpCallback...\n", 0);
+
+ //
+ // Remove this lock in the Fcb lock chain, regardlesss of the status
+ // of the IO.
+ //
+
+ RemoveLockFromFcb(
+ IrpContext->Icb->NpFcb,
+ IrpContext->Specific.Lock.FileLock );
+
+ FREE_POOL( IrpContext->Specific.Lock.FileLock );
+
+ //
+ // If any locks were pending due to a local lock conflict, try
+ // them now.
+ //
+
+ ReattemptPendingLocks(IrpContext->Icb->NpFcb);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if (!NT_SUCCESS( Status )) {
+ Error(
+ EVENT_NWRDR_FAILED_UNLOCK,
+ Status,
+ NULL,
+ 0,
+ 1,
+ IrpContext->pNpScb->ServerName.Buffer );
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return STATUS_SUCCESS;
+
+}
+
+BOOLEAN
+LockIsOverlapping(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartFileOffset,
+ ULONG Length
+ )
+/*++
+
+Routine Description:
+
+ This routine tests to see if the requested lock would overlap an
+ existing lock.
+
+ *** This routine must be called at the front of the queue.
+
+Arguments:
+
+ pNpFcb - The FCB of the file being locked.
+
+ StartFileOffset - The first byte in the range to lock.
+
+ Length - The number of bytes to lock.
+
+Return Value:
+
+ TRUE - This lock overlaps an existing lock.
+ FALSE - This lock does not overlap an existing lock.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PNW_FILE_LOCK pFileLock;
+ LONG EndFileOffset = StartFileOffset + Length - 1;
+
+ PAGED_CODE();
+
+ if ( Length == 0 ) {
+ return( FALSE );
+ }
+
+ for ( ListEntry = pNpFcb->FileLockList.Flink;
+ ListEntry != &pNpFcb->FileLockList;
+ ListEntry = ListEntry->Flink ) {
+
+ pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
+
+ //
+ // Stop the search if the current lock starts before the potential
+ // new lock ends.
+ //
+
+ if ( pFileLock->StartFileOffset > EndFileOffset ) {
+ break;
+ }
+
+ //
+ // The new lock overlaps if it starts of ends in the middle of
+ // an existing lock.
+ //
+
+ if (( StartFileOffset >= pFileLock->StartFileOffset &&
+ StartFileOffset <= pFileLock->EndFileOffset )
+ ||
+ ( EndFileOffset >= pFileLock->StartFileOffset &&
+ EndFileOffset <= pFileLock->EndFileOffset ) ) {
+
+
+ DebugTrace(0, Dbg, "Lock is overlapping\n", 0);
+ return( TRUE );
+ }
+ }
+
+ DebugTrace(0, Dbg, "Lock is NOT overlapping\n", 0);
+ return( FALSE );
+}
+
+VOID
+AddLockToFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ )
+/*++
+
+Routine Description:
+
+ This routine inserts a lock structure into the ordered list of locks
+ for this ICB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ NpFcb - The non paged FCB of file that is being locked.
+
+ FileLock - The file lock structure to insert.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PNW_FILE_LOCK pFileLock;
+
+ LONG StartFileOffset = FileLock->StartFileOffset;
+ LONG EndFileOffset = FileLock->EndFileOffset;
+
+ DebugTrace(0, Dbg, "Adding Lock to FCB %08lx\n", pNpFcb);
+ DebugTrace(0, Dbg, "Lock is %08lx\n", FileLock );
+
+ if ( IsListEmpty( &pNpFcb->FileLockList ) ) {
+ InsertHeadList( &pNpFcb->FileLockList, &FileLock->ListEntry );
+ return;
+ }
+
+ for ( ListEntry = pNpFcb->FileLockList.Flink;
+ ListEntry != &pNpFcb->FileLockList;
+ ListEntry = ListEntry->Flink ) {
+
+ pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
+
+ //
+ // Stop the search if the current lock starts after the
+ // new lock ends.
+ //
+
+ if ( pFileLock->StartFileOffset > EndFileOffset ) {
+ break;
+ }
+
+ }
+
+ //
+ // Insert the file lock into the ordered list.
+ //
+
+ InsertTailList( ListEntry, &FileLock->ListEntry );
+}
+
+
+VOID
+RemoveLockFromFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ )
+/*++
+
+Routine Description:
+
+ This routine removes a lock structure from the ordered list of locks
+ for this FCB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ pNpFcb - The non paged FCB of file that is being unlocked.
+
+ FileLock - The file lock structure to remove.
+
+Return Value:
+
+ None.
+
+--*/
+{
+#if DBG
+ PNW_FILE_LOCK foundFileLock;
+#endif
+
+ DebugTrace(0, Dbg, "Removing Lock from FCB %08lx\n", pNpFcb);
+ DebugTrace(0, Dbg, "Lock is %08lx\n", FileLock );
+
+ ASSERT( LockExists( pNpFcb, FileLock->StartFileOffset, FileLock->Length, &foundFileLock ) );
+ ASSERT( foundFileLock == FileLock );
+
+ RemoveEntryList( &FileLock->ListEntry );
+ return;
+}
+
+
+VOID
+ReattemptPendingLocks(
+ PNONPAGED_FCB pNpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine reattempts locks that are pending due to a local lock
+ conflict.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ pNpFcb - The non paged FCB of file that is being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY listEntry, nextListEntry;
+ PNW_FILE_LOCK fileLock;
+ NTSTATUS status;
+
+ DebugTrace(+1, Dbg, "ReattemptPendingLocks...\n", 0);
+
+ //
+ // Run the list of pending locks.
+ //
+
+ for ( listEntry = pNpFcb->PendingLockList.Flink;
+ listEntry != &pNpFcb->PendingLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ fileLock = CONTAINING_RECORD( listEntry, NW_FILE_LOCK, ListEntry );
+
+ if ( !LockIsOverlapping( pNpFcb, fileLock->StartFileOffset, fileLock->Length ) ) {
+
+ //
+ // It is now safe to try this lock.
+ //
+
+ RemoveEntryList( listEntry );
+
+ DebugTrace(0, Dbg, "Reattempt lock %08lx\n", fileLock->IrpContext);
+
+ status = Exchange (
+ fileLock->IrpContext,
+ LockNcpCallback,
+ "Fbrddw",
+ NCP_LOCK_RANGE,
+ fileLock->Flags | 0x01, // BUGBUG
+ fileLock->Icb->Handle, sizeof( fileLock->Icb->Handle ),
+ fileLock->StartFileOffset,
+ fileLock->Length,
+ LockTimeoutThreshold );
+
+ if ( !NT_SUCCESS( status ) ) {
+
+ NwDequeueIrpContext( fileLock->IrpContext, FALSE );
+ NwCompleteRequest( fileLock->IrpContext, status );
+
+ FREE_POOL( fileLock );
+
+ } else if ( status == STATUS_PENDING ) {
+ DebugTrace(-1, Dbg, "ReattemptPendingLocks\n", 0);
+ return;
+ }
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "ReattemptPendingLocks\n", 0);
+ return;
+}
+
+
+BOOLEAN
+LockExists(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartOffset,
+ ULONG Length,
+ PNW_FILE_LOCK *FileLock
+ )
+/*++
+
+Routine Description:
+
+ This routine test whether or not a lock is owned for this ICB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ pNpFcb - The non paged FCB of file that is being locked.
+
+ StartOffset - The starting file offset of the lock.
+
+ Length - The number of bytes to lock.
+
+ FileLock - Returns a pointer to the FileLock structure if it was found.
+
+Return Value:
+
+ TRUE - This lock is being held for this ICB.
+ FALSE - This lock is NOT being held for this ICB.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PNW_FILE_LOCK pFileLock;
+ LONG EndOffset = StartOffset + Length - 1;
+
+ for ( ListEntry = pNpFcb->FileLockList.Flink;
+ ListEntry != &pNpFcb->FileLockList;
+ ListEntry = ListEntry->Flink ) {
+
+ pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
+
+ //
+ // Search for the lock that exactly matches this one.
+ //
+
+ if ( pFileLock->StartFileOffset == StartOffset &&
+ pFileLock->EndFileOffset == EndOffset ) {
+
+ *FileLock = pFileLock;
+ DebugTrace(0, Dbg, "Found lock\n", 0);
+ return( TRUE );
+ }
+
+ }
+
+ *FileLock = NULL;
+
+ DebugTrace(0, Dbg, "Could not find lock\n", 0);
+ return( FALSE );
+}
+
+NTSTATUS
+UnlockIcbLocks(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine unlocks the first lock for an ICB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context pointers for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PICB pIcb;
+ PNW_FILE_LOCK pFileLock;
+ PLIST_ENTRY LastLockEntry;
+ NTSTATUS Status;
+ PNONPAGED_FCB pNpFcb;
+
+ DebugTrace(+1, Dbg, "UnlockIcbLocks...\n", 0);
+
+ pIcb = pIrpContext->Icb;
+ pNpFcb = pIcb->NpFcb;
+
+ LastLockEntry = pIrpContext->Specific.Lock.LastLock;
+
+ if ( LastLockEntry->Flink == &pNpFcb->FileLockList ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, STATUS_SUCCESS );
+
+ DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", 0);
+ return STATUS_PENDING;
+ }
+
+ pFileLock = CONTAINING_RECORD( LastLockEntry->Flink, NW_FILE_LOCK, ListEntry );
+
+ if ( pIrpContext->Specific.Lock.ByKey ) {
+
+ //
+ // Doing an unlock by key, skip locks that don't have a matching key.
+ //
+
+ while ( pFileLock->Key != pIrpContext->Specific.Lock.Key ) {
+
+ if ( pFileLock->ListEntry.Flink == &pNpFcb->FileLockList ) {
+
+ //
+ // FIXFIX should we return STATUS_RANGE_NOT_LOCKED if there were no matches
+ // at all?
+ //
+
+ DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", STATUS_SUCCESS);
+
+ return( STATUS_SUCCESS );
+ }
+
+ pIrpContext->Specific.Lock.LastLock = &pFileLock->ListEntry;
+ pFileLock = CONTAINING_RECORD( &pFileLock->ListEntry, NW_FILE_LOCK, ListEntry );
+ }
+
+ // We have a locked range. Proceed to unlock this one and any others.
+
+ }
+
+ RemoveEntryList( &pFileLock->ListEntry );
+
+ Status = Exchange (
+ pIrpContext,
+ UnlockIcbLocksCallback,
+ "F-rddw",
+ NCP_UNLOCK_RANGE,
+ pIcb->Handle, sizeof( pIcb->Handle ),
+ pFileLock->StartFileOffset,
+ pFileLock->Length,
+ 1 );
+
+ FREE_POOL( pFileLock );
+
+ DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", Status);
+ return Status;
+}
+
+
+NTSTATUS
+UnlockIcbLocksCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ DebugTrace(0, Dbg, "LockNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Ignore the response, plod ahead.
+ //
+
+ Status = UnlockIcbLocks( IrpContext );
+
+ return Status;
+}
+
+
+
+VOID
+NwFreeLocksForIcb(
+ IN PIRP_CONTEXT pIrpContext,
+ PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unlocks all locks held for a specific ICB.
+
+ Because its only called from Cleanup prior to a close we can
+ simply free the internal structures. The server will clear the
+ locks on the handle when it gets the close.
+
+Arguments:
+
+ ICB - The ICB to free the locks for.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PLIST_ENTRY listEntry, nextListEntry;
+ PNW_FILE_LOCK pFileLock;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFreeLockForIcb...\n", 0);
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ for ( listEntry = Icb->NpFcb->FileLockList.Flink;
+ listEntry != &Icb->NpFcb->FileLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ pFileLock = CONTAINING_RECORD(
+ listEntry,
+ NW_FILE_LOCK,
+ ListEntry );
+
+ if ( pFileLock->Icb == Icb ) {
+
+ RemoveEntryList( listEntry );
+ FREE_POOL( pFileLock );
+
+ DebugTrace( 0, Dbg, "Freed lock %08lx\n", pFileLock );
+ }
+
+ }
+
+ ReattemptPendingLocks( Icb->NpFcb );
+
+ DebugTrace(-1, Dbg, "NwFreeLockForIcb -> VOID\n", 0);
+
+}
+
diff --git a/private/nw/rdr/lockcode.c b/private/nw/rdr/lockcode.c
new file mode 100644
index 000000000..1a2941c2d
--- /dev/null
+++ b/private/nw/rdr/lockcode.c
@@ -0,0 +1,168 @@
+
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ lockcode.c
+
+Abstract:
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 30-Jan-1994
+ Manny Weiser (mannyw) 17-May-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+
+#ifndef QFE_BUILD
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwReferenceUnlockableCodeSection )
+#pragma alloc_text( PAGE, NwDereferenceUnlockableCodeSection )
+#endif
+
+extern BOOLEAN TimerStop; // From Timer.c
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+
+VOID
+NwReferenceUnlockableCodeSection (
+ VOID
+ )
+{
+ ULONG oldCount;
+
+ //
+ // Lock the lockable code database.
+ //
+
+ ExAcquireResourceExclusive( &NwUnlockableCodeResource, TRUE );
+
+ //
+ // Increment the reference count for the section.
+ //
+
+ oldCount = NwSectionDescriptor.ReferenceCount++;
+
+ if ( oldCount == 0 && NwSectionDescriptor.Handle == NULL ) {
+
+ //
+ // This is the first reference to the section. Start the timer.
+ // Lock our code.
+ //
+
+ NwSectionDescriptor.Handle = MmLockPagableCodeSection( NwSectionDescriptor.Base );
+ StartTimer( );
+
+ } else {
+
+ //
+ // This is not the first reference to the section. The section
+ // had better be locked!
+ //
+
+ ASSERT( NwSectionDescriptor.Handle != NULL );
+
+ //
+ // Restart the timer if the rdr was stopped but didn't unload.
+ //
+
+ if (TimerStop == TRUE) {
+ StartTimer();
+ }
+
+ }
+
+ DebugTrace(+0, Dbg, "NwReferenceCodeSection %d\n", NwSectionDescriptor.ReferenceCount );
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+
+ return;
+
+} // NwReferenceUnlockableCodeSection
+
+
+VOID
+NwDereferenceUnlockableCodeSection (
+ VOID
+ )
+{
+ ULONG newCount;
+
+ //
+ // Lock the lockable code database.
+ //
+
+ ExAcquireResourceExclusive( &NwUnlockableCodeResource, TRUE );
+
+ ASSERT( NwSectionDescriptor.Handle != NULL );
+ ASSERT( NwSectionDescriptor.ReferenceCount > 0 &&
+ NwSectionDescriptor.ReferenceCount < 0x7FFF );
+
+ //
+ // Decrement the reference count for the section.
+ //
+
+ newCount = --NwSectionDescriptor.ReferenceCount;
+
+ DebugTrace(+0, Dbg, "NwDereferenceCodeSection %d\n", NwSectionDescriptor.ReferenceCount );
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+
+ return;
+
+} // NwDereferenceUnlockableCodeSection
+
+BOOLEAN
+NwUnlockCodeSections(
+ IN BOOLEAN BlockIndefinitely
+ )
+{
+ //
+ // Lock the lockable code database.
+ //
+
+ if (!ExAcquireResourceExclusive( &NwUnlockableCodeResource, BlockIndefinitely )) {
+ return FALSE; // Avoid potential deadlock in timer.c
+ }
+
+ DebugTrace(+0, Dbg, "NwUnlockCodeSections %d\n", NwSectionDescriptor.ReferenceCount );
+
+ if ( NwSectionDescriptor.ReferenceCount == 0 ) {
+
+ if ( NwSectionDescriptor.Handle != NULL ) {
+
+ //
+ // This is the last reference to the section. Stop the timer and
+ // unlock the code.
+ //
+
+ StopTimer();
+
+ MmUnlockPagableImageSection( NwSectionDescriptor.Handle );
+ NwSectionDescriptor.Handle = NULL;
+
+ }
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+ return TRUE;
+ }
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+ return FALSE;
+
+}
+
+#endif
diff --git a/private/nw/rdr/makefile b/private/nw/rdr/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rdr/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/rdr/ndsfsctl.c b/private/nw/rdr/ndsfsctl.c
new file mode 100644
index 000000000..b60c98d8f
--- /dev/null
+++ b/private/nw/rdr/ndsfsctl.c
@@ -0,0 +1,2128 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsFsctl.c
+
+Abstract:
+
+ This implements the NDS user mode hooks to the redirector.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+#pragma alloc_text( PAGE, DispatchNds )
+#pragma alloc_text( PAGE, PrepareLockedBufferFromFsd )
+#pragma alloc_text( PAGE, DoBrowseFsctl )
+#pragma alloc_text( PAGE, NdsRawFragex )
+#pragma alloc_text( PAGE, NdsResolveName )
+#pragma alloc_text( PAGE, NdsGetObjectInfo )
+#pragma alloc_text( PAGE, NdsListSubordinates )
+#pragma alloc_text( PAGE, NdsReadAttributes )
+#pragma alloc_text( PAGE, NdsGetVolumeInformation )
+#pragma alloc_text( PAGE, NdsOpenStream )
+#pragma alloc_text( PAGE, NdsSetContext )
+#pragma alloc_text( PAGE, NdsGetContext )
+#pragma alloc_text( PAGE, NdsVerifyTreeHandle )
+#pragma alloc_text( PAGE, NdsGetPrintQueueInfo )
+#pragma alloc_text( PAGE, NdsChangePass )
+#pragma alloc_text( PAGE, NdsListTrees )
+
+//
+// The main handler for all NDS FSCTL calls.
+//
+
+NTSTATUS
+DispatchNds(
+ ULONG IoctlCode,
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine instigates an NDS transaction requested from
+ the fsctl interface.
+
+Arguments:
+
+ IoctlCode - Supplies the code to be used for the NDS transaction.
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transaction.
+
+--*/
+{
+ NTSTATUS Status = STATUS_NOT_SUPPORTED;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+
+ PAGED_CODE();
+
+ //
+ // Always set the user uid in the irp context so that
+ // referral creates NEVER go astray.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ IrpContext->Specific.Create.UserUid.QuadPart = Uid.QuadPart;
+
+ switch ( IoctlCode ) {
+
+ //
+ // These calls do not require us to lock down
+ // the user's buffer, but they do generate wire
+ // traffic.
+ //
+
+ case FSCTL_NWR_NDS_SETCONTEXT:
+ DebugTrace( 0, Dbg, "DispatchNds: Set Context\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GETCONTEXT:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Context\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_OPEN_STREAM:
+ DebugTrace( 0, Dbg, "DispatchNds: Open Stream\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_VERIFY_TREE:
+ DebugTrace( 0, Dbg, "DispatchNds: Verify Tree\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GET_QUEUE_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Queue Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GET_VOLUME_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Volume Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ //
+ // These four fsctl calls are the basis of browsing. They
+ // all require a request packet and a user buffer that we
+ // lock down.
+ //
+
+ case FSCTL_NWR_NDS_RESOLVE_NAME:
+ DebugTrace( 0, Dbg, "DispatchNds: Resolve Name\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_LIST_SUBS:
+ DebugTrace( 0, Dbg, "DispatchNds: List Subordinates\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_READ_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Read Object Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_READ_ATTR:
+ DebugTrace( 0, Dbg, "DispatchNds: Read Attribute\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ //
+ // Support for user mode fragment exchange.
+ //
+
+ case FSCTL_NWR_NDS_RAW_FRAGEX:
+ DebugTrace( 0, Dbg, "DispatchNds: Raw Fragex\n", 0 );
+ return NdsRawFragex( IrpContext );
+
+ //
+ // Change an NDS password.
+ //
+
+ case FSCTL_NWR_NDS_CHANGE_PASS:
+ DebugTrace( 0, Dbg, "DispatchNds: Change Password\n", 0 );
+ return NdsChangePass( IrpContext );
+
+ //
+ // Special fsctl to list the trees that a particular nt user
+ // has credentials to since the change pass ui runs under the
+ // system luid. Sigh.
+ //
+
+ case FSCTL_NWR_NDS_LIST_TREES:
+ DebugTrace( 0, Dbg, "DispatchNds: List trees\n", 0 );
+ return NdsListTrees( IrpContext );
+
+ default:
+
+ DebugTrace( 0, Dbg, "DispatchNds: No Such IOCTL\n", 0 );
+ break;
+
+ }
+
+ DebugTrace( 0, Dbg, " -> %08lx\n", Status );
+ return Status;
+
+}
+
+NTSTATUS
+PrepareLockedBufferFromFsd(
+ PIRP_CONTEXT pIrpContext,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*
+
+Description:
+
+ This routine takes the irp context for an FSD request with
+ a user mode buffer, and locks down the buffer so that it may
+ be sent to the transport. The locked down buffer, in addition
+ to being described in the irp and irp context, is described
+ in the LOCKED_BUFFER structure.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ pLockedBuffer - the locked response buffer
+
+*/
+{
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the irp and input buffer information and lock the
+ // buffer to the irp.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ if ( !OutputBufferLength ) {
+
+ DebugTrace( 0, Dbg, "No fsd buffer length in PrepareLockedBufferFromFsd...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
+
+ if ( !OutputBuffer ) {
+
+ DebugTrace( 0, Dbg, "No fsd buffer in PrepareLockedBufferFromFsd...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ pIrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ //
+ // Fill in our locked buffer description.
+ //
+
+ pLockedBuffer->pRecvBufferVa = MmGetMdlVirtualAddress( irp->MdlAddress );
+ pLockedBuffer->dwRecvLen = MdlLength( irp->MdlAddress );
+ pLockedBuffer->pRecvMdl = irp->MdlAddress;
+
+ // DebugTrace( 0, Dbg, "Locked fsd buffer at %08lx\n", pLockedBuffer->pRecvBufferVa );
+ // DebugTrace( 0, Dbg, " len -> %d\n", pLockedBuffer->dwRecvLen );
+ // DebugTrace( 0, Dbg, " recv mdl at %08lx\n", pLockedBuffer->pRecvMdl );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+DoBrowseFsctl( PIRP_CONTEXT pIrpContext,
+ ULONG IoctlCode,
+ BOOL LockdownBuffer
+)
+/*+++
+
+Description:
+
+ This actually sets up for an NDS operation that requires wire
+ traffic, including locking down the user buffer if necessary.
+
+Arguments:
+
+ pIrpContext - the irp context for this request
+ IoctlCode - the ioctl requested
+ LockdownBuffer - do we need to lock down the user buffer
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+ PNWR_NDS_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+
+ PVOID fsContext, fsObject;
+ NODE_TYPE_CODE nodeTypeCode;
+ PSCB pScb = NULL;
+ PICB pIcb = NULL;
+
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ LOCKED_BUFFER LockedBuffer;
+
+ PAGED_CODE();
+
+ //
+ // Get the request packet in the input buffer.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ InputBuffer = (PNWR_NDS_REQUEST_PACKET) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( !InputBuffer ||
+ !InputBufferLength ) {
+
+ DebugTrace( 0, Dbg, "BrowseFsctl has no input buffer...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Decode the file object and point the irp context the
+ // the appropriate connection... Should this be in an
+ // exception frame?
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsObject );
+
+ if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ pIcb = (PICB) fsObject;
+ pScb = (pIcb->SuperType).Scb;
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pIrpContext->pScb->pNpScb;
+ pIrpContext->Icb = pIcb;
+
+ }
+
+ //
+ // Lock the users buffer if this destined for the transport.
+ //
+
+ if ( LockdownBuffer &&
+ nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ Status = PrepareLockedBufferFromFsd( pIrpContext, &LockedBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // Call the appropriate browser.
+ //
+
+ switch ( IoctlCode ) {
+
+ case FSCTL_NWR_NDS_RESOLVE_NAME:
+
+ return NdsResolveName( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_LIST_SUBS:
+
+ return NdsListSubordinates( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_READ_INFO:
+
+ return NdsGetObjectInfo( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_READ_ATTR:
+
+ return NdsReadAttributes( pIrpContext, InputBuffer, &LockedBuffer );
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid ioctl for locked BrowseFsctl...\n", 0 );
+ return STATUS_NOT_SUPPORTED;
+
+ }
+
+ }
+
+ //
+ // There's no user reply buffer for these calls, hence there's no lockdown.
+ //
+
+ switch ( IoctlCode ) {
+
+ case FSCTL_NWR_NDS_OPEN_STREAM:
+
+ //
+ // There has to be an ICB for this!
+ //
+
+ if ( nodeTypeCode != NW_NTC_ICB_SCB ) {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ return NdsOpenStream( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_SETCONTEXT:
+
+ return NdsSetContext( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GETCONTEXT:
+
+ return NdsGetContext( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_VERIFY_TREE:
+
+ //
+ // Verify that this handle is valid for the specified tree.
+ //
+
+ return NdsVerifyTreeHandle( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GET_QUEUE_INFO:
+
+ //
+ // Get the queue info for this print queue.
+ //
+
+ return NdsGetPrintQueueInfo( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GET_VOLUME_INFO:
+
+ //
+ // Get the volume info for this volume object.
+ // For the new shell property sheets.
+ //
+
+ return NdsGetVolumeInformation( pIrpContext, InputBuffer );
+
+ }
+
+ //
+ // All others are not supported.
+ //
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS
+NdsRawFragex(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send a raw user requested fragment.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsObject;
+ PSCB pScb = NULL;
+ PICB pIcb = NULL;
+
+ DWORD NdsVerb;
+ LOCKED_BUFFER NdsRequest;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ PBYTE RawRequest;
+ DWORD RawRequestLen;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ RawRequestLen = irpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( !Rrp || ( RawRequestLen < sizeof( NWR_NDS_REQUEST_PACKET ) ) ) {
+
+ DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Decode the file object and point the irp context
+ // to the appropriate connection.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsObject );
+
+ if ( nodeTypeCode != NW_NTC_ICB_SCB ) {
+
+ DebugTrace( 0, Dbg, "A raw fragment request requires a server handle.\n", 0 );
+ return STATUS_INVALID_HANDLE;
+ }
+
+ pIcb = (PICB) fsObject;
+ pScb = (pIcb->SuperType).Scb;
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pIrpContext->pScb->pNpScb;
+ pIrpContext->Icb = pIcb;
+
+ //
+ // Dig out the parameters.
+ //
+
+ NdsVerb = Rrp->Parameters.RawRequest.NdsVerb;
+ RawRequestLen = Rrp->Parameters.RawRequest.RequestLength;
+ RawRequest = &Rrp->Parameters.RawRequest.Request[0];
+
+ //
+ // Get the reply buffer all locked in for the fragex.
+ //
+
+ Status = PrepareLockedBufferFromFsd( pIrpContext, &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ try {
+
+ if ( RawRequestLen ) {
+
+ Status = FragExWithWait( pIrpContext,
+ NdsVerb,
+ &NdsRequest,
+ "r",
+ RawRequest,
+ RawRequestLen );
+ } else {
+
+ Status = FragExWithWait( pIrpContext,
+ NdsVerb,
+ &NdsRequest,
+ NULL );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+ Rrp->Parameters.RawRequest.ReplyLength = NdsRequest.dwBytesWritten;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsResolveName(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*+++
+
+Description:
+
+ This function decodes the resolve name request and makes the
+ actual wire request.
+
+Parameters:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The status of the exchange.
+
+---*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING uObjectName;
+ DWORD dwResolverFlags;
+ WCHAR ObjectName[MAX_NDS_NAME_CHARS];
+
+ PNDS_WIRE_RESPONSE_RESOLVE_NAME pWireResponse;
+ PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL pReferral;
+ PNDS_RESPONSE_RESOLVE_NAME pUserResponse;
+ IPXaddress *ReferredAddress;
+ PSCB Scb, OldScb;
+
+ PAGED_CODE();
+
+ //
+ // Fill in the resolver flags and the unicode string for the
+ // object name from the request packet.
+ //
+
+ try {
+
+ uObjectName.Length = (USHORT)(pNdsRequest->Parameters).ResolveName.ObjectNameLength;
+ uObjectName.MaximumLength = sizeof( ObjectName );
+
+ if ( uObjectName.Length > sizeof( ObjectName ) ) {
+ ExRaiseStatus( STATUS_INVALID_BUFFER_SIZE );
+ }
+
+ RtlCopyMemory( ObjectName,
+ (pNdsRequest->Parameters).ResolveName.ObjectName,
+ uObjectName.Length );
+
+ uObjectName.Buffer = ObjectName;
+
+ dwResolverFlags = (pNdsRequest->Parameters).ResolveName.ResolverFlags;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in resolving name.\n", 0 );
+ return GetExceptionCode();
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_RESOLVE_NAME,
+ pLockedBuffer,
+ "DDDSDDDD",
+ 0, // version
+ dwResolverFlags, // flags
+ 0, // scope
+ &uObjectName, // distinguished name
+ 1,0, // transport type
+ 1,0 ); // treeWalker type
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // We need to convert the NDS_WIRE_RESPONSE_RESOLVE_NAME that
+ // we got from the server into an NDS_RESPONSE_RESOLVE_NAME
+ // for more general consumption. Notice that a referral packet
+ // has an additional DWORD in it - what a pain.
+ //
+
+ pWireResponse = (PNDS_WIRE_RESPONSE_RESOLVE_NAME) pLockedBuffer->pRecvBufferVa;
+ pReferral = (PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL) pLockedBuffer->pRecvBufferVa;
+ pUserResponse = (PNDS_RESPONSE_RESOLVE_NAME) pLockedBuffer->pRecvBufferVa;
+
+ try {
+
+ if ( pWireResponse->RemoteEntry == RESOLVE_NAME_ACCEPT_REMOTE ) {
+
+ //
+ // This server can handle this request.
+ //
+
+ pUserResponse->ServerNameLength = 0;
+ (pNdsRequest->Parameters).ResolveName.BytesWritten = 4 * sizeof( DWORD );
+
+ } else {
+
+ ASSERT( pWireResponse->RemoteEntry == RESOLVE_NAME_REFER_REMOTE );
+
+ ASSERT( pReferral->ServerAddresses == 1 );
+ ASSERT( pReferral->AddressType == 0 );
+ ASSERT( pReferral->AddressLength == sizeof( IPXaddress ) );
+
+ //
+ // We've been referred to another server. We have to connect
+ // to the referred server to get the name for the caller.
+ //
+
+ ReferredAddress = (IPXaddress *) pReferral->Address;
+
+ OldScb = pIrpContext->pScb;
+
+ //
+ // Dequeue us from our original server. Do not defer the
+ // logon at this point since a referral means we're in the
+ // middle of a browse operation.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &Scb,
+ pIrpContext,
+ NULL,
+ ReferredAddress,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ RtlCopyMemory( pUserResponse->ReferredServer,
+ Scb->pNpScb->ServerName.Buffer,
+ Scb->pNpScb->ServerName.Length );
+
+ pUserResponse->ServerNameLength = Scb->pNpScb->ServerName.Length;
+ (pNdsRequest->Parameters).ResolveName.BytesWritten =
+ ( 4 * sizeof( DWORD ) ) + Scb->pNpScb->ServerName.Length;
+
+ DebugTrace( 0, Dbg, "Resolve name referral to: %wZ\n",
+ &Scb->pNpScb->ServerName );
+
+ //
+ // Restore the server pointers, we're not ready to jump
+ // servers yet since this might be a request from the fsd.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( Scb->pNpScb );
+ pIrpContext->pScb = OldScb;
+ pIrpContext->pNpScb = OldScb->pNpScb;
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in resolving name.\n", 0 );
+ return GetExceptionCode();
+
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NdsGetObjectInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ Get the basic object information for the listed object.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD dwObjId;
+
+ PAGED_CODE();
+
+ //
+ // Get the object id from the users request packet.
+ //
+
+ try {
+ dwObjId = (pNdsRequest->Parameters).GetObjectInfo.ObjectId;
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer in NdsGetObjectId...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+ }
+
+ //
+ // Hit the wire.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ pLockedBuffer,
+ "DD",
+ 0,
+ dwObjId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).GetObjectInfo.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after getting object info...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsListSubordinates(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ List the immediate subordinates of an object.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD dwParent, dwIterHandle;
+
+ PAGED_CODE();
+
+ //
+ // Dig out the request parameters.
+ //
+
+ try {
+
+ dwParent = (pNdsRequest->Parameters).ListSubordinates.ObjectId;
+ dwIterHandle = (pNdsRequest->Parameters).ListSubordinates.IterHandle;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! No user mode buffer in ListSubordinates...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+
+ //
+ // Make the request.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_LIST,
+ pLockedBuffer,
+ "DDDD",
+ 0,
+ 0x40,
+ dwIterHandle,
+ dwParent );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).ListSubordinates.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after getting subordinate list...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadAttributes(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ Retrieve the named attribute of an object.
+
+ BUGBUG: We don't really know the max attribute name size.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+
+ DWORD dwIterHandle, dwOid;
+ UNICODE_STRING uAttributeName;
+ WCHAR AttributeName[MAX_NDS_NAME_CHARS];
+
+ PAGED_CODE();
+
+ RtlZeroMemory( AttributeName, sizeof( AttributeName ) );
+
+ try {
+
+ uAttributeName.Length = (USHORT)(pNdsRequest->Parameters).ReadAttribute.AttributeNameLength;
+ uAttributeName.MaximumLength = sizeof( AttributeName );
+
+ if ( uAttributeName.Length > uAttributeName.MaximumLength ) {
+ ExRaiseStatus( STATUS_INVALID_BUFFER_SIZE );
+ }
+
+ RtlCopyMemory( AttributeName,
+ (pNdsRequest->Parameters).ReadAttribute.AttributeName,
+ uAttributeName.Length );
+
+ uAttributeName.Buffer = AttributeName;
+
+ dwIterHandle = (pNdsRequest->Parameters).ReadAttribute.IterHandle;
+ dwOid = (pNdsRequest->Parameters).ReadAttribute.ObjectId;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "Bonk! Exception accessing user mode buffer in read attributes...\n", 0 );
+ return GetExceptionCode();
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ,
+ pLockedBuffer,
+ "DDDDDDS",
+ 0, // version
+ dwIterHandle, // iteration handle
+ dwOid, // object id
+ 1, // info type
+ //
+ // The attribute specifier has been seen at zero and
+ // at 0x4e0000. I don't know why... but zero doesn't
+ // work sometimes...
+ //
+ 0x4e0000, // attrib type
+ 1, // number of attribs
+ &uAttributeName ); // attrib name
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).ReadAttribute.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after reading attribute...\n", 0 );
+ return GetExceptionCode();
+
+ }
+
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetVolumeInformation(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+)
+/*+++
+
+Description:
+
+ This function gets the name of the server that hosts
+ the listed nds volume.
+
+Parameters:
+
+ pIrpContext - describes the irp for this request
+ pNdsRequest - the request parameters
+
+---*/
+{
+
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PSCB pOriginalScb;
+ PBYTE OutputBuffer;
+ ULONG OutputBufferLength;
+
+ UNICODE_STRING VolumeObject;
+ DWORD VolumeOid;
+ UNICODE_STRING HostServerAttr;
+ UNICODE_STRING HostVolumeAttr;
+ UNICODE_STRING Attribute;
+
+ PWCHAR ServerString;
+ ULONG ServerLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the irp and output buffer information.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
+ }
+
+ //
+ // Prepare the input information.
+ //
+
+ VolumeObject.Length = (USHORT)pNdsRequest->Parameters.GetVolumeInfo.VolumeNameLen;
+ VolumeObject.MaximumLength = VolumeObject.Length;
+ VolumeObject.Buffer = &(pNdsRequest->Parameters.GetVolumeInfo.VolumeName[0]);
+
+ DebugTrace( 0, Dbg, "Retrieving volume info for %wZ\n", &VolumeObject );
+
+ HostServerAttr.Buffer = HOST_SERVER_ATTRIBUTE; // L"Host Server"
+ HostServerAttr.Length = sizeof( HOST_SERVER_ATTRIBUTE ) - sizeof( WCHAR );
+ HostServerAttr.MaximumLength = HostServerAttr.Length;
+
+ HostVolumeAttr.Buffer = HOST_VOLUME_ATTRIBUTE; // L"Host Resource Name"
+ HostVolumeAttr.Length = sizeof( HOST_VOLUME_ATTRIBUTE ) - sizeof( WCHAR );
+ HostVolumeAttr.MaximumLength = HostVolumeAttr.Length;
+
+ try {
+
+ //
+ // NdsResolveNameKm may have to jump servers to service this
+ // request, however it's dangerous for us to derefence the original
+ // scb because that would expose a scavenger race condition. So,
+ // we add an additional ref-count to the original scb and then clean
+ // up appropriately afterwards, depending on whether or not we
+ // jumped servers.
+ //
+
+ pOriginalScb = pIrpContext->pScb;
+
+ NwReferenceScb( pOriginalScb->pNpScb );
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ &VolumeObject,
+ &VolumeOid,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status )) {
+ NwDereferenceScb( pOriginalScb->pNpScb );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ if ( pIrpContext->pScb == pOriginalScb ) {
+
+ //
+ // We didn't jump servers.
+ //
+
+ NwDereferenceScb( pOriginalScb->pNpScb );
+ }
+
+ //
+ // We have to read the server into a temporary buffer so
+ // we can strip off the x500 prefix and the context
+ // from the server name. This isn't really what I would
+ // call nice, but it's the way Netware works.
+ //
+
+ Attribute.Length = 0;
+ Attribute.MaximumLength = MAX_NDS_NAME_SIZE;
+ Attribute.Buffer = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE );
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ VolumeOid,
+ &HostServerAttr,
+ &Attribute );
+
+ if ( !NT_SUCCESS( Status )) {
+ FREE_POOL( Attribute.Buffer );
+ goto CleanupScbReferences;
+ }
+
+ ServerString = Attribute.Buffer;
+
+ while( Attribute.Length ) {
+
+ if ( *ServerString == L'=' ) {
+ ServerString += 1;
+ Attribute.Length -= sizeof( WCHAR );
+ break;
+ }
+
+ ServerString += 1;
+ Attribute.Length -= sizeof( WCHAR );
+ }
+
+ if ( Attribute.Length == 0 ) {
+ DebugTrace( 0, Dbg, "Malformed server for volume.\n", 0 );
+ FREE_POOL( Attribute.Buffer );
+ Status = STATUS_UNSUCCESSFUL;
+ goto CleanupScbReferences;
+ }
+
+ ServerLength = 0;
+
+ while ( ServerLength < (Attribute.Length / sizeof( WCHAR )) ) {
+
+ if ( ServerString[ServerLength] == L'.' ) {
+ break;
+ }
+
+ ServerLength++;
+ }
+
+ if ( ServerLength == ( Attribute.Length / sizeof( WCHAR ) ) ) {
+ DebugTrace( 0, Dbg, "Malformed server for volume.\n", 0 );
+ FREE_POOL( Attribute.Buffer );
+ Status = STATUS_UNSUCCESSFUL;
+ goto CleanupScbReferences;
+ }
+
+ ServerLength *= sizeof( WCHAR );
+ RtlCopyMemory( OutputBuffer, ServerString, ServerLength );
+
+ pNdsRequest->Parameters.GetVolumeInfo.ServerNameLen = ServerLength;
+
+ FREE_POOL( Attribute.Buffer );
+
+ Attribute.Length = Attribute.MaximumLength = (USHORT)ServerLength;
+ Attribute.Buffer = (PWCHAR)OutputBuffer;
+ DebugTrace( 0, Dbg, "Host server is: %wZ\n", &Attribute );
+
+ //
+ // Now do the volume in place. This is the easy one.
+ //
+
+ Attribute.MaximumLength = (USHORT)( OutputBufferLength - ServerLength );
+ Attribute.Buffer = (PWSTR) ( OutputBuffer + ServerLength );
+ Attribute.Length = 0;
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ VolumeOid,
+ &HostVolumeAttr,
+ &Attribute );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto CleanupScbReferences;
+ }
+
+ pNdsRequest->Parameters.GetVolumeInfo.TargetVolNameLen = Attribute.Length;
+ DebugTrace( 0, Dbg, "Host volume is: %wZ\n", &Attribute );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Exception handling user mode buffer in GetVolumeInfo.\n", 0 );
+ goto CleanupScbReferences;
+
+ }
+
+ Status = STATUS_SUCCESS;
+
+CleanupScbReferences:
+
+ if ( pIrpContext->pScb != pOriginalScb ) {
+
+ //
+ // We jumped servers and have to cleanup.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pScb->pNpScb );
+ pIrpContext->pScb = pOriginalScb;
+ pIrpContext->pNpScb = pOriginalScb->pNpScb;
+
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsOpenStream(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ UNICODE_STRING uStream;
+ WCHAR StreamName[MAX_NDS_NAME_CHARS];
+
+ LOCKED_BUFFER NdsRequest;
+
+ DWORD dwOid, StreamAccess;
+ DWORD hNwHandle, dwFileLen;
+
+ PICB pIcb;
+ PSCB pScb = pIrpContext->pNpScb->pScb;
+
+ BOOLEAN LicensedConnection = FALSE;
+
+ PAGED_CODE();
+
+ pIcb = pIrpContext->Icb;
+
+ uStream.Length = 0;
+ uStream.MaximumLength = sizeof( StreamName );
+ uStream.Buffer = StreamName;
+
+ DebugTrace( 0 , Dbg, "NDS open stream...\n", 0 );
+
+ try {
+
+ dwOid = (pNdsRequest->Parameters).OpenStream.ObjectOid;
+ StreamAccess = (pNdsRequest->Parameters).OpenStream.StreamAccess;
+ RtlCopyUnicodeString( &uStream, &(pNdsRequest->Parameters).OpenStream.StreamName );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "Bonk! Bad user mode buffer in open stream.\n", 0 );
+ return GetExceptionCode();
+ }
+
+ //
+ // We have the oid and stream name; let's get the handle.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If we haven't licensed this connection yet, it's time. Get to the
+ // head of the queue to protect the SCB fields and authenticate the
+ // connection (do not defer the login).
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ ASSERT( pScb->MajorVersion > 3 );
+
+ if ( ( pScb->UserName.Length == 0 ) &&
+ ( pScb->VcbCount == 0 ) &&
+ ( pScb->OpenNdsStreams == 0 ) ) {
+
+ if ( pScb->pNpScb->State != SCB_STATE_IN_USE ) {
+
+ Status = ConnectScb( &pScb,
+ pIrpContext,
+ &(pScb->pNpScb->ServerName),
+ NULL, // address
+ NULL, // name
+ NULL, // password
+ FALSE, // defer login
+ FALSE, // delete connection
+ TRUE ); // existing scb
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Couldn't connect server %08lx to open NDS stream.\n", pScb );
+ goto ExitWithCleanup;
+ }
+ }
+
+ ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ goto ExitWithCleanup;
+ }
+
+ LicensedConnection = TRUE;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_OPEN_STREAM,
+ &NdsRequest,
+ "DDDs",
+ 0, // version
+ StreamAccess, // file access
+ dwOid, // object id
+ &uStream ); // attribute name
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_DD",
+ sizeof( DWORD ), // completion code
+ &hNwHandle, // remote handle
+ &dwFileLen ); // file length
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ *(WORD *)(&pIcb->Handle[0]) = (WORD)hNwHandle + 1;
+ *( (UNALIGNED DWORD *) (&pIcb->Handle[2]) ) = hNwHandle;
+
+ pIrpContext->pScb->OpenNdsStreams++;
+
+ DebugTrace( 0, Dbg, "File stream opened. Length = %d\n", dwFileLen );
+
+ (pNdsRequest->Parameters).OpenStream.FileLength = dwFileLen;
+ pIcb->HasRemoteHandle = TRUE;
+
+ pIcb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+
+ if ( ( !NT_SUCCESS( Status ) ) &&
+ ( LicensedConnection ) ) {
+ NdsUnlicenseConnection( pIrpContext );
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return Status;
+
+}
+
+NTSTATUS
+NdsSetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ PLOGON pLogon;
+
+ UNICODE_STRING Tree, Context;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "NDS set context.\n", 0 );
+
+ //
+ // Find out who this is.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !Logon ) {
+
+ DebugTrace( 0, Dbg, "Couldn't find logon data for this user.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+
+ }
+
+ //
+ // Verify that this context really is a context.
+ //
+
+ Tree.Length = (USHORT)(pNdsRequest->Parameters).SetContext.TreeNameLen;
+ Tree.MaximumLength = Tree.Length;
+ Tree.Buffer = (pNdsRequest->Parameters).SetContext.TreeAndContextString;
+
+ Context.Length = (USHORT)(pNdsRequest->Parameters).SetContext.ContextLen;
+ Context.MaximumLength = Context.Length;
+ Context.Buffer = (WCHAR *) (((BYTE *)Tree.Buffer) + Tree.Length);
+
+ Status = NdsVerifyContext( pIrpContext, &Tree, &Context );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Status = NdsLookupCredentials( &Tree,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No credentials in set context.\n", 0 );
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+ //
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( Context.Length > MAX_NDS_NAME_SIZE ) {
+
+ DebugTrace( 0, Dbg, "Context too long.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ try {
+
+ RtlCopyUnicodeString( &pCredentials->CurrentContext, &Context );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in SetContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "New context: %wZ\n", &Context );
+ return STATUS_SUCCESS;
+
+ReleaseAndExit:
+
+ NwReleaseCredList( pLogon );
+ return Status;
+}
+
+NTSTATUS
+NdsGetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ PLOGON pLogon;
+
+ UNICODE_STRING Tree;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "NDS get context.\n", 0 );
+
+ //
+ // Find out who this is.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !Logon ) {
+
+ DebugTrace( 0, Dbg, "Couldn't find logon data for this user.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+
+ }
+
+ //
+ // We know who it is, so get the context.
+ //
+
+ Tree.Length = (USHORT)(pNdsRequest->Parameters).GetContext.TreeNameLen;
+ Tree.MaximumLength = Tree.Length;
+ Tree.Buffer = (pNdsRequest->Parameters).GetContext.TreeNameString;
+
+ Status = NdsLookupCredentials( &Tree,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // No context has been set, so report none.
+ //
+
+ try {
+
+ (pNdsRequest->Parameters).GetContext.Context.Length = 0;
+ return STATUS_SUCCESS;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in GetContext.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ }
+
+ //
+ // Make sure we can report the whole thing.
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( (pNdsRequest->Parameters).GetContext.Context.MaximumLength <
+ pCredentials->CurrentContext.Length ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ReleaseAndExit;
+ }
+
+ try {
+
+ RtlCopyUnicodeString( &(pNdsRequest->Parameters).GetContext.Context,
+ &pCredentials->CurrentContext );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in GetContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "Reported context: %wZ\n", &pCredentials->CurrentContext );
+ return STATUS_SUCCESS;
+
+ReleaseAndExit:
+
+ NwReleaseCredList( pLogon );
+ return Status;
+
+}
+
+NTSTATUS
+NdsVerifyTreeHandle(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[NDS_TREE_NAME_LEN];
+
+ PAGED_CODE();
+
+ try {
+
+ //
+ // Check to see if the handle points to a dir server in the
+ // specified tree. Make sure to unmunge the tree name in
+ // the SCB first, just in case.
+ //
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ UnmungeCredentialName( &pIrpContext->pScb->NdsTreeName,
+ &NdsTree );
+
+ if ( !RtlCompareUnicodeString( &NdsTree,
+ &(pNdsRequest->Parameters).VerifyTree.TreeName,
+ TRUE ) ) {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Success\n", 0 );
+ Status = STATUS_SUCCESS;
+ } else {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Failure\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Invalid parameters.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetPrintQueueInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ UNICODE_STRING ServerAttribute;
+ WCHAR Server[] = L"Host Server";
+
+ PSCB pPrintHost = NULL;
+ PNONPAGED_SCB pOriginalNpScb = NULL;
+
+ DWORD dwObjectId, dwObjectType;
+
+ UNICODE_STRING uPrintServer;
+
+ BYTE *pbQueue, *pbRQueue;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &ServerAttribute, Server );
+
+ //
+ // Make sure we have a print queue object. We may
+ // have to jump servers if we get referred to another
+ // replica. If this is the case, we can't lose the
+ // ref count on the original server since that's where
+ // the ICB handle is.
+ //
+
+ pOriginalNpScb = pIrpContext->pNpScb;
+ NwReferenceScb( pOriginalNpScb );
+
+ Status = NdsVerifyObject( pIrpContext,
+ &(pNdsRequest->Parameters).GetQueueInfo.QueueName,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectId,
+ &dwObjectType );
+
+ if ( pIrpContext->pNpScb == pOriginalNpScb ) {
+
+ //
+ // If we were not referred, remove the extra ref
+ // count and clear the original pointer.
+ //
+
+ NwDereferenceScb( pOriginalNpScb );
+ pOriginalNpScb = NULL;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Retrieve the host server name.
+ //
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectId,
+ &ServerAttribute,
+ &(pNdsRequest->Parameters).GetQueueInfo.HostServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the actual server name from the X.500 name.
+ //
+
+ Status = NdsGetServerBasicName( &(pNdsRequest->Parameters).GetQueueInfo.HostServer,
+ &uPrintServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Connect to the actual host server. If there was a referral, we
+ // can simply dump the referred server since we are holding the ref
+ // count on the original owner of the ICB.
+ //
+
+ if ( pOriginalNpScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ } else {
+ pOriginalNpScb = pIrpContext->pNpScb;
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &pPrintHost,
+ pIrpContext,
+ &uPrintServer,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pIrpContext->pNpScb = NULL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Re-query the OID of the print queue object on this server.
+ // Don't allow any server jumping this time; we only need the
+ // oid of the queue.
+ //
+
+ Status = NdsVerifyObject( pIrpContext,
+ &(pNdsRequest->Parameters).GetQueueInfo.QueueName,
+ FALSE,
+ RSLV_CREATE_ID,
+ &dwObjectId,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Byte swap the queue id.
+ //
+
+ pbRQueue = (BYTE *) &dwObjectId;
+ pbQueue = (BYTE *) &(pNdsRequest->Parameters).GetQueueInfo.QueueId;
+
+ pbQueue[0] = pbRQueue[3];
+ pbQueue[1] = pbRQueue[2];
+ pbQueue[2] = pbRQueue[1];
+ pbQueue[3] = pbRQueue[0];
+ }
+
+ExitWithCleanup:
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Restore the pointers and ref counts as appropriate.
+ //
+
+ if ( pOriginalNpScb ) {
+
+ if ( pIrpContext->pNpScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ }
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalNpScb->pScb;
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsChangePass(
+ PIRP_CONTEXT pIrpContext
+) {
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ UNICODE_STRING NdsTree;
+ UNICODE_STRING UserName;
+ UNICODE_STRING CurrentPassword;
+ UNICODE_STRING NewPassword;
+ PBYTE CurrentString;
+ BOOLEAN ServerReferenced = FALSE;
+
+ OEM_STRING OemCurrentPassword;
+ BYTE CurrentBuffer[MAX_PW_CHARS];
+
+ OEM_STRING OemNewPassword;
+ BYTE NewBuffer[MAX_PW_CHARS];
+
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ if ( !Rrp ) {
+
+ DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Dig out the parameters.
+ //
+
+ CurrentString = ( PBYTE ) &(Rrp->Parameters.ChangePass.StringBuffer[0]);
+
+ NdsTree.Length = NdsTree.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.NdsTreeNameLength;
+ NdsTree.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += NdsTree.Length;
+
+ UserName.Length = UserName.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.UserNameLength;
+ UserName.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += UserName.Length;
+
+ CurrentPassword.Length = CurrentPassword.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.CurrentPasswordLength;
+ CurrentPassword.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += CurrentPassword.Length;
+
+ NewPassword.Length = NewPassword.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.NewPasswordLength;
+ NewPassword.Buffer = ( PWCHAR ) CurrentString;
+
+ //
+ // Get a server to handle this request.
+ //
+
+ try {
+
+ //
+ // Convert the passwords to the appropriate type.
+ //
+
+ OemCurrentPassword.Length = 0;
+ OemCurrentPassword.MaximumLength = sizeof( CurrentBuffer );
+ OemCurrentPassword.Buffer = CurrentBuffer;
+
+ OemNewPassword.Length = 0;
+ OemNewPassword.MaximumLength = sizeof( NewBuffer );
+ OemNewPassword.Buffer = NewBuffer;
+
+ RtlUpcaseUnicodeStringToOemString( &OemCurrentPassword,
+ &CurrentPassword,
+ FALSE );
+
+ RtlUpcaseUnicodeStringToOemString( &OemNewPassword,
+ &NewPassword,
+ FALSE );
+
+ //
+ // Get a dir server to handle the request.
+ //
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &Scb,
+ &NdsTree,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No dir servers for nds change password.\n", 0 );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ ServerReferenced = TRUE;
+
+ //
+ // Perform the change password.
+ //
+
+ Status = NdsTreeLogin( pIrpContext,
+ &UserName,
+ &OemCurrentPassword,
+ &OemNewPassword,
+ NULL );
+
+ NwDereferenceScb( Scb->pNpScb );
+ ServerReferenced = FALSE;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "NdsChangePass: Exception dealing with user request.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "NdsChangePassword succeeded for %wZ.\n", &UserName );
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( ServerReferenced ) {
+ NwDereferenceScb( Scb->pNpScb );
+ }
+
+ //
+ // We get STATUS_PASSWORD_EXPIRED when the user is not allowed
+ // to change their password on the Netware server, so we return
+ // PASSWORD_RESTRICTION instead.
+ //
+
+ if ( Status == STATUS_PASSWORD_EXPIRED ) {
+ Status = STATUS_PASSWORD_RESTRICTION;
+ }
+
+ return Status;
+
+
+}
+
+
+NTSTATUS
+NdsListTrees(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+Description:
+
+ This odd little routine takes the NTUSER name of the logged in
+ user (on the system) and returns a list of NDS trees that the
+ NTUSER is connected to and the user names for those connections.
+ This is necessary because the change password ui runs in the
+ systems luid and can't access the GET_CONN_STATUS api and because
+ the change password code might happen when no user is logged in.
+
+ The return data in the users buffer is an array of
+ CONN_INFORMATION structures with the strings packed after the
+ structures. There is no continuation of this routine, so pass
+ a decent sized buffer.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD OutputBufferLength;
+ PBYTE OutputBuffer;
+
+ UNICODE_STRING NtUserName;
+ PLOGON pLogon;
+ DWORD dwTreesReturned = 0;
+ DWORD dwBytesNeeded;
+
+ PCONN_INFORMATION pConnInfo;
+ PLIST_ENTRY pNdsList;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ if ( !Rrp || !OutputBufferLength || !OutputBuffer ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Dig out the parameters.
+ //
+
+ NtUserName.Length = NtUserName.MaximumLength = (USHORT) Rrp->Parameters.ListTrees.NtUserNameLength;
+ NtUserName.Buffer = &(Rrp->Parameters.ListTrees.NtUserName[0]);
+
+ DebugTrace( 0, Dbg, "ListTrees: Looking up %wZ\n", &NtUserName );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUserByName( &NtUserName );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "ListTrees: No such NT user.\n", 0 );
+ return STATUS_NO_SUCH_USER;
+ }
+
+ //
+ // Otherwise build the list of trees.
+ //
+
+ Rrp->Parameters.ListTrees.UserLuid = pLogon->UserUid;
+
+ NwAcquireExclusiveCredList( pLogon );
+ pConnInfo = ( PCONN_INFORMATION ) OutputBuffer;
+
+ pNdsList = pLogon->NdsCredentialList.Flink;
+
+ try {
+
+ while ( pNdsList != &(pLogon->NdsCredentialList) ) {
+
+ pNdsContext = CONTAINING_RECORD( pNdsList, NDS_SECURITY_CONTEXT, Next );
+
+ //
+ // Check to make sure there's a credential.
+ //
+
+ if ( pNdsContext->Credential == NULL ) {
+ goto ProcessNextListEntry;
+ }
+
+ //
+ // Check to make sure there's space to report.
+ //
+
+ dwBytesNeeded = ( sizeof( CONN_INFORMATION ) +
+ pNdsContext->Credential->userNameLength +
+ pNdsContext->NdsTreeName.Length -
+ sizeof( WCHAR ) );
+
+ if ( OutputBufferLength < dwBytesNeeded ) {
+ break;
+ }
+
+ //
+ // Report it! Note that the user name in the credential is NULL terminated.
+ //
+
+ pConnInfo->HostServerLength = pNdsContext->NdsTreeName.Length;
+ pConnInfo->UserNameLength = pNdsContext->Credential->userNameLength - sizeof( WCHAR );
+ pConnInfo->HostServer = (LPWSTR) ( ((BYTE *)pConnInfo) + sizeof( CONN_INFORMATION ) );
+ pConnInfo->UserName = (LPWSTR) ( ( (BYTE *)pConnInfo) +
+ sizeof( CONN_INFORMATION ) +
+ pConnInfo->HostServerLength );
+
+ RtlCopyMemory( pConnInfo->HostServer,
+ pNdsContext->NdsTreeName.Buffer,
+ pConnInfo->HostServerLength );
+
+ RtlCopyMemory( pConnInfo->UserName,
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize ),
+ pConnInfo->UserNameLength );
+
+ OutputBufferLength -= dwBytesNeeded;
+ dwTreesReturned++;
+ pConnInfo = ( PCONN_INFORMATION ) ( ((BYTE *)pConnInfo) + dwBytesNeeded );
+
+ProcessNextListEntry:
+
+ //
+ // Do the next one.
+ //
+
+ pNdsList = pNdsList->Flink;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If we access violate, stop and return what we have.
+ //
+
+ DebugTrace( 0, Dbg, "User mode buffer access problem.\n", 0 );
+ }
+
+ NwReleaseCredList( pLogon );
+
+ DebugTrace( 0, Dbg, "Returning %d tree entries.\n", dwTreesReturned );
+ Rrp->Parameters.ListTrees.TreesReturned = dwTreesReturned;
+ return STATUS_SUCCESS;
+}
diff --git a/private/nw/rdr/ndslogin.c b/private/nw/rdr/ndslogin.c
new file mode 100644
index 000000000..953a4a4d2
--- /dev/null
+++ b/private/nw/rdr/ndslogin.c
@@ -0,0 +1,3365 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsLogin.c
+
+Abstract:
+
+ This file implements the functionality required to
+ perform an NDS login.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+//
+// Pageable.
+//
+
+#pragma alloc_text( PAGE, NdsCanonUserName )
+#pragma alloc_text( PAGE, NdsCheckCredentials )
+#pragma alloc_text( PAGE, NdsCheckCredentialsEx )
+#pragma alloc_text( PAGE, NdsLookupCredentials )
+#pragma alloc_text( PAGE, NdsGetCredentials )
+#pragma alloc_text( PAGE, DoNdsLogon )
+#pragma alloc_text( PAGE, BeginLogin )
+#pragma alloc_text( PAGE, FinishLogin )
+#pragma alloc_text( PAGE, ChangeNdsPassword )
+#pragma alloc_text( PAGE, NdsServerAuthenticate )
+#pragma alloc_text( PAGE, BeginAuthenticate )
+#pragma alloc_text( PAGE, NdsLicenseConnection )
+#pragma alloc_text( PAGE, NdsUnlicenseConnection )
+#pragma alloc_text( PAGE, NdsGetBsafeKey )
+
+//
+// Note pageable:
+//
+// NdsTreeLogin (holds a spin lock)
+// NdsLogoff (holds a spin lock)
+//
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+);
+
+NTSTATUS
+NdsCanonUserName(
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN OUT PUNICODE_STRING puCanonUserName
+)
+/*+++
+
+ Canonicalize the user name for the given tree and
+ current connection state. Canonicalization includes
+ handling the correct context and cleaning off all
+ the X500 prefixes.
+
+ ALERT! The credential list must be held (shared or
+ exclusive) while this function is called.
+
+---*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ USHORT CurrentTargetIndex;
+ int PrefixBytes;
+
+ UNICODE_STRING UnstrippedName;
+ PWCHAR CanonBuffer;
+
+ PAGED_CODE();
+
+ CanonBuffer = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE );
+ if ( !CanonBuffer ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If the name starts with a dot, it's referenced from the root
+ // of the tree and we should not append the context. We should,
+ // however, strip off the leading dot so that name resolution
+ // will work.
+ //
+
+ if ( puUserName->Buffer[0] == L'.' ) {
+
+ UnstrippedName.Length = puUserName->Length - sizeof( WCHAR );
+ UnstrippedName.MaximumLength = UnstrippedName.Length;
+ UnstrippedName.Buffer = &(puUserName->Buffer[1]);
+
+ goto StripPrefixes;
+ }
+
+ //
+ // If the name contains any dots, it's qualified and we
+ // should probably just use it as is.
+ //
+
+ CurrentTargetIndex= 0;
+
+ while ( CurrentTargetIndex< ( puUserName->Length / sizeof( WCHAR ) ) ) {
+
+ if ( puUserName->Buffer[CurrentTargetIndex] == L'.' ) {
+
+ UnstrippedName.Length = puUserName->Length;
+ UnstrippedName.MaximumLength = puUserName->Length;
+ UnstrippedName.Buffer = puUserName->Buffer;
+
+ goto StripPrefixes;
+ }
+
+ CurrentTargetIndex++;
+ }
+
+ //
+ // If we have a context for this tree and the name isn't
+ // qualified, we should append the context.
+ //
+
+ if ( pNdsContext->CurrentContext.Length ) {
+
+ if ( ( puUserName->Length +
+ pNdsContext->CurrentContext.Length ) >= MAX_NDS_NAME_SIZE ) {
+
+ DebugTrace( 0, Dbg, "NDS canon name too long.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ RtlCopyMemory( CanonBuffer, puUserName->Buffer, puUserName->Length );
+ CanonBuffer[puUserName->Length / sizeof( WCHAR )] = L'.';
+
+ RtlCopyMemory( ((BYTE *)CanonBuffer) + puUserName->Length + sizeof( WCHAR ),
+ pNdsContext->CurrentContext.Buffer,
+ pNdsContext->CurrentContext.Length );
+
+ UnstrippedName.Length = puUserName->Length +
+ pNdsContext->CurrentContext.Length +
+ sizeof( WCHAR );
+ UnstrippedName.MaximumLength = MAX_NDS_NAME_SIZE;
+ UnstrippedName.Buffer = CanonBuffer;
+
+ goto StripPrefixes;
+
+ }
+
+ //
+ // It wasn't qualified, nor was there a context to append, so fail it.
+ //
+
+ DebugTrace( 0, Dbg, "The name %wZ is not canonicalizable.\n", puUserName );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+StripPrefixes:
+
+ //
+ // All of these indexes are in BYTES, not WCHARS!
+ //
+
+ CurrentTargetIndex = 0;
+ PrefixBytes = 0;
+ puCanonUserName->Length = 0;
+
+ while ( ( CurrentTargetIndex < UnstrippedName.Length ) &&
+ ( puCanonUserName->Length < puCanonUserName->MaximumLength ) ) {
+
+ //
+ // Strip off the X.500 prefixes.
+ //
+
+ if ( UnstrippedName.Buffer[CurrentTargetIndex / sizeof( WCHAR )] == L'=' ) {
+
+ CurrentTargetIndex += sizeof( WCHAR );
+ puCanonUserName->Length -= PrefixBytes;
+ PrefixBytes = 0;
+
+ continue;
+ }
+
+ puCanonUserName->Buffer[puCanonUserName->Length / sizeof( WCHAR )] =
+ UnstrippedName.Buffer[CurrentTargetIndex / sizeof( WCHAR )];
+
+ puCanonUserName->Length += sizeof( WCHAR );
+ CurrentTargetIndex += sizeof( WCHAR );
+
+ if ( UnstrippedName.Buffer[CurrentTargetIndex / sizeof( WCHAR )] == L'.' ) {
+ PrefixBytes = 0;
+ PrefixBytes -= sizeof( WCHAR );
+ } else {
+ PrefixBytes += sizeof( WCHAR );
+ }
+ }
+
+ DebugTrace( 0, Dbg, "Canonicalized name: %wZ\n", puCanonUserName );
+
+ExitWithCleanup:
+
+ FREE_POOL( CanonBuffer );
+ return Status;
+}
+
+NTSTATUS
+NdsCheckCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+)
+/*++
+
+ Given a set of credentials and a username and password,
+ we need to determine if username and password match those
+ that were used to acquire the credentials.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ //
+ // Grab the user's LOGON structure and credentials.
+ //
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "Invalid client security context in NdsCheckCredentials.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+ }
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if( NT_SUCCESS( Status ) ) {
+
+ if ( pCredentials->CredentialLocked ) {
+
+ Status = STATUS_DEVICE_BUSY;
+
+ } else {
+
+ Status = NdsCheckCredentialsEx( pIrpContext,
+ pLogon,
+ pCredentials,
+ puUserName,
+ puPassword );
+
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsCheckCredentialsEx(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+)
+/*++
+
+ Given a set of credentials and a username and password,
+ we need to determine if username and password match those
+ that were used to acquire the credentials.
+
+ ALERT! The credential list must be held (either shared or
+ exclusive) while this function is called.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNICODE_STRING CredentialName;
+
+ UNICODE_STRING CanonCredentialName, CanonUserName;
+ PWCHAR CredNameBuffer;
+ PWCHAR UserNameBuffer;
+
+ UNICODE_STRING StoredPassword;
+ PWCHAR Stored;
+
+ PAGED_CODE();
+
+ //
+ // If we haven't logged into to the tree, there is no security
+ // conflict. Otherwise, run the check.
+ //
+
+ ASSERT ( pNdsContext->Credential != NULL );
+
+ CredNameBuffer = ALLOCATE_POOL( PagedPool,
+ ( 2 * MAX_NDS_NAME_SIZE ) +
+ ( MAX_PW_CHARS * sizeof( WCHAR ) ) );
+ if ( !CredNameBuffer ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ UserNameBuffer = (PWCHAR) (((BYTE *)CredNameBuffer) + MAX_NDS_NAME_SIZE );
+ Stored = (PWCHAR) (((BYTE *)UserNameBuffer) + MAX_NDS_NAME_SIZE );
+
+ if ( puUserName && puUserName->Length ) {
+
+ //
+ // Canon the incoming name and the credential name.
+ //
+
+ CanonUserName.Length = 0;
+ CanonUserName.MaximumLength = MAX_NDS_NAME_SIZE;
+ CanonUserName.Buffer = UserNameBuffer;
+
+ Status = NdsCanonUserName( pNdsContext,
+ puUserName,
+ &CanonUserName );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
+ goto ExitWithCleanup;
+ }
+
+ CanonCredentialName.Length = 0;
+ CanonCredentialName.MaximumLength = MAX_NDS_NAME_SIZE;
+ CanonCredentialName.Buffer = CredNameBuffer;
+
+ CredentialName.Length = (USHORT)pNdsContext->Credential->userNameLength - sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (PWCHAR)( (PBYTE)(pNdsContext->Credential) +
+ sizeof( NDS_CREDENTIAL ) );
+
+ Status = NdsCanonUserName( pNdsContext,
+ &CredentialName,
+ &CanonCredentialName );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // See if they match.
+ //
+
+ if ( RtlCompareUnicodeString( &CanonUserName, &CanonCredentialName, TRUE )) {
+ DebugTrace( 0, Dbg, "NdsCheckCredentialsEx: user name conflict.\n", 0 );
+ Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
+ goto ExitWithCleanup;
+ }
+ }
+
+ if ( puPassword && puPassword->Length ) {
+
+ //
+ // Now check the password.
+ //
+
+ StoredPassword.Length = 0;
+ StoredPassword.MaximumLength = MAX_PW_CHARS * sizeof( WCHAR );
+ StoredPassword.Buffer = Stored;
+
+ RtlOemStringToUnicodeString( &StoredPassword,
+ &pNdsContext->Password,
+ FALSE );
+
+ if ( RtlCompareUnicodeString( puPassword,
+ &StoredPassword,
+ TRUE ) ) {
+ DebugTrace( 0, Dbg, "NdsCheckCredentialsEx: password conflict.\n", 0 );
+ Status = STATUS_WRONG_PASSWORD;
+ goto ExitWithCleanup;
+ }
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( CredNameBuffer );
+ return Status;
+}
+
+NTSTATUS
+NdsLookupCredentials(
+ IN PUNICODE_STRING puTreeName,
+ IN PLOGON pLogon,
+ OUT PNDS_SECURITY_CONTEXT *ppCredentials,
+ DWORD dwDesiredAccess,
+ BOOLEAN fCreate
+)
+/*+++
+
+ Retrieve the nds credentials for the given tree from the
+ list of valid credentials for the specified user.
+
+ puTreeName - The name of the tree that we want credentials for. If NULL
+ is specified, we return the credentials for the default tree.
+ pLogon - The logon structure for the user we want to access the tree.
+ ppCredentials - Where to put the pointed to the credentials.
+ dwDesiredAccess - CREDENTIAL_READ if we want read/only access, CREDENTIAL_WRITE
+ if we're going to change the credentials.
+ fCreate - If the credentials don't exist, should we create them?
+
+ We return the credentials with the list held in the appropriate mode. The
+ caller is responsible for releasing the list when done with the credentials.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PLIST_ENTRY pFirst, pNext;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+
+ //
+ // Check for existing credentials.
+ //
+
+ if ( dwDesiredAccess == CREDENTIAL_READ ) {
+ NwAcquireSharedCredList( pLogon );
+ } else {
+ NwAcquireExclusiveCredList( pLogon );
+ }
+
+ pFirst = &pLogon->NdsCredentialList;
+ pNext = pLogon->NdsCredentialList.Flink;
+
+ while ( pNext && ( pFirst != pNext ) ) {
+
+ pNdsContext = (PNDS_SECURITY_CONTEXT)
+ CONTAINING_RECORD( pNext,
+ NDS_SECURITY_CONTEXT,
+ Next );
+
+ ASSERT( pNdsContext->ntc == NW_NTC_NDS_CREDENTIAL );
+
+ if ( !puTreeName ||
+ !RtlCompareUnicodeString( puTreeName,
+ &pNdsContext->NdsTreeName,
+ TRUE ) ) {
+
+ //
+ // If the tree name is null, we'll return the first one
+ // on the list. Otherwise this will work as normal.
+ //
+
+ *ppCredentials = pNdsContext;
+ return STATUS_SUCCESS;
+ }
+
+ pNext = pNdsContext->Next.Flink;
+
+ }
+
+ //
+ // We didn't find the credential. Should we create it?
+ //
+
+ NwReleaseCredList( pLogon );
+
+ if ( !fCreate || !puTreeName ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Acquire exclusive since we're mucking with the list.
+ //
+
+ NwAcquireExclusiveCredList( pLogon );
+
+ pNdsContext = ( PNDS_SECURITY_CONTEXT )
+ ALLOCATE_POOL( PagedPool, sizeof( NDS_SECURITY_CONTEXT ) );
+
+ if ( !pNdsContext ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsLookupCredentials.\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto UnlockAndExit;
+ }
+
+ //
+ // Initialize the structure.
+ //
+
+ RtlZeroMemory( pNdsContext, sizeof( NDS_SECURITY_CONTEXT ) );
+ pNdsContext->ntc = NW_NTC_NDS_CREDENTIAL;
+ pNdsContext->nts = sizeof( NDS_SECURITY_CONTEXT );
+
+ //
+ // Initialize the tree name.
+ //
+
+ pNdsContext->NdsTreeName.MaximumLength = sizeof( pNdsContext->NdsTreeNameBuffer );
+ pNdsContext->NdsTreeName.Buffer = pNdsContext->NdsTreeNameBuffer;
+
+ RtlCopyUnicodeString( &pNdsContext->NdsTreeName, puTreeName );
+
+ //
+ // Initialize the context buffer.
+ //
+
+ pNdsContext->CurrentContext.Length = 0;
+ pNdsContext->CurrentContext.MaximumLength = sizeof( pNdsContext->CurrentContextString );
+ pNdsContext->CurrentContext.Buffer = pNdsContext->CurrentContextString;
+
+ //
+ // Insert the context into the list.
+ //
+
+ InsertHeadList( &pLogon->NdsCredentialList, &pNdsContext->Next );
+ *ppCredentials = pNdsContext;
+
+ //
+ // Release and re-acquire with the correct permissions.
+ //
+
+ NwReleaseCredList( pLogon );
+
+ if ( dwDesiredAccess == CREDENTIAL_READ ) {
+ NwAcquireSharedCredList( pLogon );
+ } else {
+ NwAcquireExclusiveCredList( pLogon );
+ }
+
+ //
+ // There's no chance that someone's going to come in during this
+ // small window and do a logout because there's no login data
+ // in the credentials.
+ //
+
+ return STATUS_SUCCESS;
+
+UnlockAndExit:
+
+ NwReleaseCredList( pLogon );
+ return STATUS_UNSUCCESSFUL;
+
+}
+
+NTSTATUS
+NdsGetCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+)
+/*++
+
+ Do an NDS tree login and aquire a valid set of credentials.
+
+--*/
+{
+ NTSTATUS Status;
+
+ USHORT i;
+ UNICODE_STRING LoginName, LoginPassword;
+ PWCHAR NdsName;
+ PWCHAR NdsPassword;
+
+ OEM_STRING OemPassword;
+ PBYTE OemPassBuffer;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ //
+ // Prepare our login name by canonicalizing the supplied user
+ // name or using a default user name if appropriate.
+ //
+
+ NdsName = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE +
+ MAX_PW_CHARS * sizeof( WCHAR ) +
+ MAX_PW_CHARS );
+
+ if ( !NdsName ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NdsPassword = (PWCHAR) (((BYTE *) NdsName) + MAX_NDS_NAME_SIZE );
+ OemPassBuffer = ((BYTE *) NdsPassword ) + ( MAX_PW_CHARS * sizeof( WCHAR ) );
+
+ LoginName.Length = 0;
+ LoginName.MaximumLength = MAX_NDS_NAME_SIZE;
+ LoginName.Buffer = NdsName;
+
+ Status = NdsLookupCredentials( &pIrpContext->pScb->NdsTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If the credential list is locked, someone is logging
+ // out and we have to fail the request.
+ //
+
+ if ( pNdsContext->CredentialLocked ) {
+
+ Status = STATUS_DEVICE_BUSY;
+ NwReleaseCredList( pLogon );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Fix up the user name.
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( puUserName && puUserName->Buffer ) {
+
+ Status = NdsCanonUserName( pNdsContext,
+ puUserName,
+ &LoginName );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ } else {
+
+ //
+ // There's no name, so try the default name in the
+ // current context.
+ //
+
+ if ( pNdsContext->CurrentContext.Length > 0 ) {
+
+ //
+ // Make sure the lengths fit and all that.
+ //
+
+ if ( ( pLogon->UserName.Length +
+ pNdsContext->CurrentContext.Length ) >= LoginName.MaximumLength ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto NameResolved;
+ }
+
+ RtlCopyMemory( LoginName.Buffer, pLogon->UserName.Buffer, pLogon->UserName.Length );
+ LoginName.Buffer[pLogon->UserName.Length / sizeof( WCHAR )] = L'.';
+
+ RtlCopyMemory( ((BYTE *)LoginName.Buffer) + pLogon->UserName.Length + sizeof( WCHAR ),
+ pNdsContext->CurrentContext.Buffer,
+ pNdsContext->CurrentContext.Length );
+
+ LoginName.Length = pLogon->UserName.Length +
+ pNdsContext->CurrentContext.Length +
+ sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, "Using default name and context for login: %wZ\n", &LoginName );
+
+ } else {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ }
+
+NameResolved:
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "No name in NdsGetCredentials.\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If there's a password, use it. Otherwise, use the default password.
+ //
+
+ if ( puPassword && puPassword->Buffer ) {
+
+ LoginPassword.Length = puPassword->Length;
+ LoginPassword.MaximumLength = puPassword->MaximumLength;
+ LoginPassword.Buffer = puPassword->Buffer;
+
+ } else {
+
+ LoginPassword.Length = 0;
+ LoginPassword.MaximumLength = MAX_PW_CHARS * sizeof( WCHAR );
+ LoginPassword.Buffer = NdsPassword;
+
+ RtlCopyUnicodeString( &LoginPassword,
+ &pLogon->PassWord );
+ }
+
+ //
+ // Convert the password to upcase OEM and login.
+ //
+
+ OemPassword.Length = 0;
+ OemPassword.MaximumLength = MAX_PW_CHARS;
+ OemPassword.Buffer = OemPassBuffer;
+
+ Status = RtlUpcaseUnicodeStringToOemString( &OemPassword,
+ &LoginPassword,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_WRONG_PASSWORD;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsTreeLogin( pIrpContext, &LoginName, &OemPassword, NULL, pLogon );
+
+ExitWithCleanup:
+
+ FREE_POOL( NdsName );
+ return Status;
+}
+
+NTSTATUS
+DoNdsLogon(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+)
+/*+++
+
+Description:
+
+ This is the lead function for handling login and authentication to
+ Netware Directory Services. This function acquires credentials to
+ the appropriate tree for the server that the irp context points to,
+ logging us into that tree if necessary, and authenticates us to the
+ current server.
+
+ BUGBUG: This routine gets called from reconnect attempts and from
+ normal requests. Since the allowable actions on each of these paths
+ are different, it might make sense to have two routines, each
+ more maintainable than this single routine. For now, watch out for
+ code in the RECONNECT_ATTEMPT cases.
+
+Arguments:
+
+ pIrpContext - irp context; must refer to appropriate server
+ UserName - login username
+ Password - password
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pCredentials;
+ PSCB pScb;
+ UNICODE_STRING BinderyName;
+ UNICODE_STRING uUserName;
+ UNICODE_STRING NtGroup;
+ USHORT Length;
+ PSCB pOriginalServer = NULL;
+ DWORD UserOID;
+
+ BOOL AtHeadOfQueue = FALSE;
+ BOOL HoldingCredentialResource = FALSE;
+ BOOL PasswordExpired = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // Get to the head of the queue if we need to.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+ } else {
+ NwAppendToQueueAndWait( pIrpContext );
+ }
+
+ AtHeadOfQueue = TRUE;
+
+ //
+ // Grab the user's logon structure.
+ //
+
+ pScb = pIrpContext->pScb;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+
+ DebugTrace( 0, Dbg, "Invalid client security context.\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Login and then re-get the tree credentials.
+ //
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ HoldingCredentialResource = FALSE;
+ goto LOGIN;
+ }
+
+ HoldingCredentialResource = TRUE;
+
+ //
+ // Are we logged in? We can't hold the
+ // credential list while logging in!!
+ //
+
+ if ( !pCredentials->Credential ) {
+
+ HoldingCredentialResource = FALSE;
+ NwReleaseCredList( pLogon );
+ goto LOGIN;
+ }
+
+ //
+ // If this credential is locked, we fail!
+ //
+
+ if ( pCredentials->CredentialLocked ) {
+ Status = STATUS_DEVICE_BUSY;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCheckCredentialsEx( pIrpContext,
+ pLogon,
+ pCredentials,
+ UserName,
+ Password );
+
+ if( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ goto AUTHENTICATE;
+
+LOGIN:
+
+ ASSERT( HoldingCredentialResource == FALSE );
+
+ //
+ // If this is a reconnect attempt and we don't have credentials
+ // already, we have to give up. We can't acquire credentials
+ // during a reconnect and retry because it could cause a deadlock.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsGetCredentials( pIrpContext,
+ pLogon,
+ UserName,
+ Password );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If this credential is locked, someone is
+ // already logging out and so we fail this.
+ //
+
+ if ( pCredentials->CredentialLocked ) {
+ Status = STATUS_DEVICE_BUSY;
+ NwReleaseCredList( pLogon );
+ goto ExitWithCleanup;
+ }
+
+ HoldingCredentialResource = TRUE;
+
+AUTHENTICATE:
+
+ ASSERT( HoldingCredentialResource == TRUE );
+ ASSERT( AtHeadOfQueue == TRUE );
+
+ //
+ // NdsServerAuthenticate will not take us off the
+ // head of the queue since this is not allowed.
+ //
+
+ Status = NdsServerAuthenticate( pIrpContext, pCredentials );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ NwReleaseCredList( pLogon );
+ HoldingCredentialResource = FALSE;
+
+ //
+ // If this is a gateway request and is not a reconnect attempt, we
+ // need to check the group membership.
+ //
+
+ if ( pIrpContext->Specific.Create.UserUid.QuadPart == DefaultLuid.QuadPart) {
+
+ if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ AtHeadOfQueue = FALSE;
+
+ //
+ // Resolve the name, allowing a server jump if necessary.
+ //
+
+ pOriginalServer = pIrpContext->pScb;
+ NwReferenceScb( pOriginalServer->pNpScb );
+
+ uUserName.MaximumLength = pCredentials->Credential->userNameLength;
+ uUserName.Length = uUserName.MaximumLength;
+ uUserName.Buffer = ( WCHAR * ) ( ((BYTE *)pCredentials->Credential) +
+ sizeof( NDS_CREDENTIAL ) +
+ pCredentials->Credential->optDataSize );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ &uUserName,
+ &UserOID,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ RtlInitUnicodeString( &NtGroup, NT_GATEWAY_GROUP );
+ Status = NdsCheckGroupMembership( pIrpContext, UserOID, &NtGroup );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Gateway connection to NDS server not allowed.\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Take us off the head of the queue in case were were left there.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Restore us to the server that we authenticated to so that
+ // the irp context refers to the authenticated server.
+ //
+
+ if ( pOriginalServer != NULL ) {
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+
+ if ( pIrpContext->pScb != pOriginalServer ) {
+ pIrpContext->pScb = pOriginalServer;
+ pIrpContext->pNpScb = pOriginalServer->pNpScb;
+ }
+ }
+
+ }
+ }
+
+ExitWithCleanup:
+
+ if ( HoldingCredentialResource ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( AtHeadOfQueue ) {
+
+ //
+ // If we failed and this was a reconnect attempt, don't dequeue the
+ // irp context or we may deadlock when we try to do the bindery logon.
+ // See ReconnectRetry() for more information on this restriction.
+ //
+
+ if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ }
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( PasswordExpired ) ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ }
+
+ DebugTrace( 0, Dbg, "DoNdsLogin: Status = %08lx\n", Status );
+ return Status;
+}
+
+NTSTATUS
+NdsTreeLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUser,
+ IN POEM_STRING pOemPassword,
+ IN POEM_STRING pOemNewPassword,
+ IN PLOGON pUserLogon
+)
+/*++
+
+Routine Description:
+
+ Login the specified user to the NDS tree at the server referred
+ to by the given IrpContext using the supplied password.
+
+Arguments:
+
+ pIrpContext - The irp context for this server connection.
+ puUser - The user login name.
+ pOemPassword - The upper-case, plaintext password.
+ pOemNewPassword - The new password for a change pass request.
+ pUserLogon - The LOGON security structure for this user,
+ which may be NULL for a change password
+ request.
+
+Side Effects:
+
+ If successful, the user's credentials, signature, and
+ public key are saved in the nds context for this NDS tree
+ in the credential list in the LOGON structure.
+
+Notes:
+
+ This function may have to jump around a few servers to
+ get all the info needed for login. If restores the irp
+ context to the original server so that when we authenticate,
+ we authenticate to the correct server (as requested by the
+ user).
+
+--*/
+{
+ NTSTATUS Status; // status of the operation
+ int CryptStatus; // crypt status
+
+ DWORD dwChallenge; // four byte server challenge
+ PUNICODE_STRING puServerName; // server's distinguished name
+
+ DWORD dwUserOID, // user oid on the current server
+ dwSrcUserOID, // user oid on the originating server
+ dwServerOID; // server oid
+
+ BYTE *pbServerPublicNdsKey, // server's public key in NDS format
+ *pbServerPublicBsafeKey; // server's public BSAFE key
+
+ int cbServerPublicNdsKeyLen, // length of server public NDS key
+ cbServerPublicBsafeKeyLen; // length of server pubilc BSAFE key
+
+ BYTE *pbUserPrivateNdsKey, // user's private key in NDS format
+ *pbUserPrivateBsafeKey; // user's private BSAFE key
+
+ int cbUserPrivateNdsKeyLen; // length of user private NDS key
+ WORD cbUserPrivateBsafeKeyLen; // length of user private BSAFE key
+
+ BYTE pbNw3PasswdHash[16]; // nw3 passwd hash
+ BYTE pbNewPasswdHash[16]; // new passwd hash for change pass
+ BYTE pbPasswdHashRC2Key[8]; // RC2 secret key generated from hash
+
+ BYTE pbEncryptedChallenge[16]; // RC2 encrypted server challenge
+ int cbEncryptedChallengeLen; // length of the encrypted challenge
+
+ PNDS_SECURITY_CONTEXT psNdsSecurityContext; // user's nds context
+ BYTE *pbSignData; // user's signature data
+
+ UNICODE_STRING uUserDN; // users fully distinguished name
+ PWCHAR UserDnBuffer;
+
+ DWORD dwValidityStart, dwValidityEnd;
+ BOOLEAN AtHeadOfQueue = FALSE;
+ BOOLEAN HoldingCredResource = FALSE;
+ BOOLEAN PasswordExpired = FALSE;
+
+ UNICODE_STRING PlainServerName;
+ USHORT UidLen;
+ KIRQL OldIrql;
+ PSCB pLoginServer = NULL;
+ PSCB pOriginalServer = NULL;
+ DWORD dwLoginFlags = 0;
+
+ DebugTrace( 0, Dbg, "Enter NdsTreeLogin...\n", 0 );
+
+ ASSERT( puUser );
+ ASSERT( pOemPassword );
+
+ //
+ // Allocate space for the server's public key and the user's private key.
+ //
+
+ cbServerPublicNdsKeyLen = MAX_PUBLIC_KEY_LEN + MAX_ENC_PRIV_KEY_LEN + MAX_NDS_NAME_SIZE;
+
+ pbServerPublicNdsKey = ALLOCATE_POOL( PagedPool, cbServerPublicNdsKeyLen );
+
+ if ( !pbServerPublicNdsKey ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // First, jump to a server where we can get this user object.
+ // Don't forget the server to which we were originally pointed.
+ //
+
+ pOriginalServer = pIrpContext->pScb;
+ NwReferenceScb( pOriginalServer->pNpScb );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ puUser,
+ &dwUserOID,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Now get the user name from the object info.
+ //
+
+ UserDnBuffer = (PWCHAR) ( pbServerPublicNdsKey +
+ MAX_PUBLIC_KEY_LEN +
+ MAX_ENC_PRIV_KEY_LEN );
+
+ uUserDN.Length = 0;
+ uUserDN.MaximumLength = MAX_NDS_NAME_SIZE;
+ uUserDN.Buffer = UserDnBuffer;
+
+ Status = NdsGetUserName( pIrpContext,
+ dwUserOID,
+ &uUserDN );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the name of the server we are currently on. We borrow a
+ // little space from our key buffer and overwrite it later.
+ //
+
+ puServerName = ( PUNICODE_STRING ) pbServerPublicNdsKey;
+ puServerName->Buffer = (PWCHAR) pbServerPublicNdsKey + sizeof( UNICODE_STRING );
+ puServerName->MaximumLength = cbServerPublicNdsKeyLen - sizeof( UNICODE_STRING );
+
+ Status = NdsGetServerName( pIrpContext,
+ puServerName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If the public key for this server is on a partition that's
+ // on another server, we'll have to jump over there to get the
+ // public key and then return. The key and user object are
+ // only any good on this server! DO NOT CHANGE THE ORDER OF
+ // THIS OR IT WILL BREAK!
+ //
+
+ pLoginServer = pIrpContext->pScb;
+ NwReferenceScb( pLoginServer->pNpScb );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ puServerName,
+ &dwServerOID,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the server's public key and its length.
+ //
+
+ Status = NdsReadPublicKey( pIrpContext,
+ dwServerOID,
+ pbServerPublicNdsKey,
+ &cbServerPublicNdsKeyLen );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Return us to the login server.
+ //
+
+ if ( pLoginServer != pIrpContext->pScb ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pNpScb );
+ pIrpContext->pScb = pLoginServer;
+ pIrpContext->pNpScb = pLoginServer->pNpScb;
+
+ } else {
+
+ NwDereferenceScb( pLoginServer->pNpScb );
+ }
+
+ pLoginServer = NULL;
+
+ //
+ // Locate the BSAFE key in the NDS key.
+ //
+
+ cbServerPublicBsafeKeyLen = NdsGetBsafeKey( pbServerPublicNdsKey,
+ cbServerPublicNdsKeyLen,
+ &pbServerPublicBsafeKey );
+
+ if ( !cbServerPublicBsafeKeyLen ) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Send the begin login packet. This returns to us the
+ // 4 byte challenge and the object id of the user's account
+ // on the server on which it was created. It may be the
+ // same as the object id that we provided if the account
+ // was created on this server.
+ //
+
+ Status = BeginLogin( pIrpContext,
+ dwUserOID,
+ &dwSrcUserOID,
+ &dwChallenge );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Compute the 16 byte NW3 hash and generate the
+ // 8 byte secret key from it. The 8 byte secret
+ // key consists of a MAC checksum of the NW3 hash.
+ //
+
+ Shuffle( (UCHAR *)&dwSrcUserOID,
+ pOemPassword->Buffer,
+ pOemPassword->Length,
+ pbNw3PasswdHash );
+
+ GenKey8( pbNw3PasswdHash,
+ sizeof( pbNw3PasswdHash ),
+ pbPasswdHashRC2Key );
+
+ //
+ // RC2 Encrypt the 4 byte challenge using the secret
+ // key generated from the password.
+ //
+
+ CryptStatus = CBCEncrypt( pbPasswdHashRC2Key,
+ NULL,
+ (BYTE *)&dwChallenge,
+ 4,
+ pbEncryptedChallenge,
+ &cbEncryptedChallengeLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "CBC encryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ pbUserPrivateNdsKey = pbServerPublicNdsKey + MAX_PUBLIC_KEY_LEN;
+ cbUserPrivateNdsKeyLen = MAX_ENC_PRIV_KEY_LEN;
+
+ //
+ // Make the finish login packet. If successful, this routine
+ // returns the encrypted user private key and the valid duration
+ // of the user's credentials.
+ //
+
+ if ( pOemNewPassword ) {
+ dwLoginFlags = 1;
+ }
+
+ Status = FinishLogin( pIrpContext,
+ dwUserOID,
+ dwLoginFlags,
+ pbEncryptedChallenge,
+ pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen,
+ pbUserPrivateNdsKey,
+ &cbUserPrivateNdsKeyLen,
+ &dwValidityStart,
+ &dwValidityEnd );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( !pOemNewPassword ) {
+
+ //
+ // If the password is expired, report it to the user.
+ //
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ //
+ // Allocate the credential and set up space for the private key.
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+ AtHeadOfQueue = TRUE;
+
+ Status = NdsLookupCredentials( &pIrpContext->pScb->NdsTreeName,
+ pUserLogon,
+ &psNdsSecurityContext,
+ CREDENTIAL_WRITE,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // ALERT! We are holding the credential list.
+ //
+
+ HoldingCredResource = TRUE;
+
+ psNdsSecurityContext->Credential = ALLOCATE_POOL( PagedPool,
+ sizeof( NDS_CREDENTIAL ) +
+ uUserDN.Length );
+
+ if ( !psNdsSecurityContext->Credential ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for credential)...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ *( (UNALIGNED DWORD *) &( psNdsSecurityContext->Credential->validityBegin ) ) = dwValidityStart;
+ *( (UNALIGNED DWORD *) &( psNdsSecurityContext->Credential->validityEnd ) ) = dwValidityEnd;
+
+ DebugTrace( 0, Dbg, "Credential validity start: 0x%08lx\n", dwValidityStart );
+ DebugTrace( 0, Dbg, "Credential validity end: 0x%08lx\n", dwValidityEnd );
+
+ //
+ // RC2 Decrypt the response to extract the BSAFE private
+ // key data in place.
+ //
+
+ CryptStatus = CBCDecrypt( pbPasswdHashRC2Key,
+ NULL,
+ pbUserPrivateNdsKey,
+ cbUserPrivateNdsKeyLen,
+ pbUserPrivateNdsKey,
+ &cbUserPrivateNdsKeyLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "CBC decryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Skip over the header.
+ //
+
+ pbUserPrivateBsafeKey = ( pbUserPrivateNdsKey + sizeof( TAG_DATA_HEADER ) );
+ cbUserPrivateBsafeKeyLen = *( ( WORD * ) pbUserPrivateBsafeKey );
+ pbUserPrivateBsafeKey += sizeof( WORD );
+
+ //
+ // Create the credential.
+ //
+
+ psNdsSecurityContext->Credential->tdh.version = 1;
+ psNdsSecurityContext->Credential->tdh.tag = TAG_CREDENTIAL;
+
+ GenRandomBytes( ( BYTE * ) &(psNdsSecurityContext->Credential->random),
+ sizeof( psNdsSecurityContext->Credential->random ) );
+
+ psNdsSecurityContext->Credential->optDataSize = 0;
+ psNdsSecurityContext->Credential->userNameLength = uUserDN.Length;
+
+ RtlCopyMemory( ( (BYTE *)psNdsSecurityContext->Credential) + sizeof( NDS_CREDENTIAL ),
+ UserDnBuffer,
+ uUserDN.Length );
+
+ //
+ // Generate and save the signature.
+ //
+
+ psNdsSecurityContext->Signature = ALLOCATE_POOL( PagedPool, MAX_SIGNATURE_LEN );
+
+ if ( !psNdsSecurityContext->Signature ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for signature)...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ pbSignData = ( ( ( BYTE * ) psNdsSecurityContext->Signature ) +
+ sizeof( NDS_SIGNATURE ) );
+
+ RtlZeroMemory( pbSignData, MAX_RSA_BYTES );
+
+ psNdsSecurityContext->Signature->tdh.version = 1;
+ psNdsSecurityContext->Signature->tdh.tag = TAG_SIGNATURE;
+
+ //
+ // Create the hash for the signature from the credential.
+ //
+
+ MD2( (BYTE *) psNdsSecurityContext->Credential,
+ sizeof( NDS_CREDENTIAL ) + ( uUserDN.Length ),
+ pbSignData );
+
+ //
+ // Compute the 'signature' by RSA-encrypting the
+ // 16-byte signature hash with the private key.
+ //
+
+ psNdsSecurityContext->Signature->signDataLength = RSAPrivate( pbUserPrivateBsafeKey,
+ cbUserPrivateBsafeKeyLen,
+ pbSignData,
+ 16,
+ pbSignData );
+
+ if ( !psNdsSecurityContext->Signature->signDataLength ) {
+
+ DebugTrace( 0, Dbg, "RSA private encryption for signature failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Round up the signature length, cause that's how VLM stores it.
+ //
+
+ psNdsSecurityContext->Signature->signDataLength =
+ ROUNDUP4( psNdsSecurityContext->Signature->signDataLength );
+
+ DebugTrace( 0, Dbg, "Signature data length: %d\n",
+ psNdsSecurityContext->Signature->signDataLength );
+
+ //
+ // Get the user's public key for storage in the nds context.
+ //
+
+ psNdsSecurityContext->PublicNdsKey = ALLOCATE_POOL( PagedPool, MAX_PUBLIC_KEY_LEN );
+
+ if ( !psNdsSecurityContext->PublicNdsKey ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for public key)...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ psNdsSecurityContext->PublicKeyLen = MAX_PUBLIC_KEY_LEN;
+
+ ASSERT( AtHeadOfQueue );
+ ASSERT( HoldingCredResource );
+
+ Status = NdsReadPublicKey( pIrpContext,
+ dwUserOID,
+ psNdsSecurityContext->PublicNdsKey,
+ &(psNdsSecurityContext->PublicKeyLen) );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Store away the password we used to connect.
+ //
+
+ psNdsSecurityContext->Password.Buffer = ALLOCATE_POOL( PagedPool, pOemPassword->Length );
+
+ if ( !psNdsSecurityContext->Password.Buffer ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for password)\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ psNdsSecurityContext->Password.Length = pOemPassword->Length;
+ psNdsSecurityContext->Password.MaximumLength = pOemPassword->Length;
+ RtlCopyMemory( psNdsSecurityContext->Password.Buffer,
+ pOemPassword->Buffer,
+ pOemPassword->Length );
+
+ //
+ // We are logged in to the NDS tree. Should we zero the private
+ // key, or is NT's protection sufficient?
+ //
+
+ NwReleaseCredList( pUserLogon );
+
+ //
+ // Try to elect this server as the preferred server if necessary.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if ( ( pUserLogon->ServerName.Length == 0 ) &&
+ ( !pIrpContext->Specific.Create.fExCredentialCreate ) ) {
+
+ //
+ // Strip off the unicode uid from the server name.
+ //
+
+ PlainServerName.Length = pIrpContext->pScb->UidServerName.Length;
+ PlainServerName.Buffer = pIrpContext->pScb->UidServerName.Buffer;
+
+ UidLen = 0;
+
+ while ( UidLen < ( PlainServerName.Length / sizeof( WCHAR ) ) ) {
+
+ if ( PlainServerName.Buffer[UidLen++] == L'\\' ) {
+ break;
+ }
+ }
+
+ PlainServerName.Buffer += UidLen;
+ PlainServerName.Length -= ( UidLen * sizeof( WCHAR ) );
+ PlainServerName.MaximumLength = PlainServerName.Length;
+
+ if ( PlainServerName.Length ) {
+
+ Status = SetUnicodeString( &(pUserLogon->ServerName),
+ PlainServerName.Length,
+ PlainServerName.Buffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Electing preferred server: %wZ\n", &PlainServerName );
+
+ //
+ // Increase the Scb ref count, set the preferred server flag,
+ // and move the scb to the head of the SCB list.
+ //
+
+ NwReferenceScb( pIrpContext->pScb->pNpScb );
+ pIrpContext->pScb->PreferredServer = TRUE;
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ RemoveEntryList( &(pIrpContext->pScb->pNpScb->ScbLinks) );
+ InsertHeadList( &ScbQueue, &(pIrpContext->pScb->pNpScb->ScbLinks) );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // This isn't a login, but a change password request.
+ //
+ // First we have to RC2 Decrypt the response to extract
+ // the BSAFE private key data in place (just like for a
+ // login).
+ //
+
+ CryptStatus = CBCDecrypt( pbPasswdHashRC2Key,
+ NULL,
+ pbUserPrivateNdsKey,
+ cbUserPrivateNdsKeyLen,
+ pbUserPrivateNdsKey,
+ &cbUserPrivateNdsKeyLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "CBC decryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Now, compute the hash of the new password.
+ //
+
+ Shuffle( (UCHAR *)&dwSrcUserOID,
+ pOemNewPassword->Buffer,
+ pOemNewPassword->Length,
+ pbNewPasswdHash );
+
+ //
+ // And finally, make the request.
+ //
+
+ Status = ChangeNdsPassword( pIrpContext,
+ dwUserOID,
+ dwChallenge,
+ pbNw3PasswdHash,
+ pbNewPasswdHash,
+ ( PNDS_PRIVATE_KEY ) pbUserPrivateNdsKey,
+ pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Change NDS password failed!\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ }
+
+ //
+ // Return us to our original server if we've jumped around.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ if ( pIrpContext->pScb != pOriginalServer ) {
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+ pIrpContext->pScb = pOriginalServer;
+ pIrpContext->pNpScb = pOriginalServer->pNpScb;
+
+ } else {
+
+ NwDereferenceScb( pOriginalServer->pNpScb );
+ }
+
+ pOriginalServer = NULL;
+
+ if ( !pOemNewPassword ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ FREE_POOL( pbServerPublicNdsKey );
+
+ if ( PasswordExpired ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ return Status;
+
+ExitWithCleanup:
+
+ DebugTrace( 0, Dbg, "NdsTreeLogin seems to have failed... cleaning up.\n", 0 );
+
+ FREE_POOL( pbServerPublicNdsKey );
+
+ if ( pLoginServer ) {
+ NwDereferenceScb( pLoginServer->pNpScb );
+ }
+
+ //
+ // If we failed after jumping servers, we have to restore
+ // the irp context to the original server.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ if ( pOriginalServer ) {
+
+ if ( pIrpContext->pScb != pOriginalServer ) {
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+ pIrpContext->pScb = pOriginalServer;
+ pIrpContext->pNpScb = pOriginalServer->pNpScb;
+
+ } else {
+
+ NwDereferenceScb( pOriginalServer->pNpScb );
+ }
+
+ }
+
+ if ( HoldingCredResource ) {
+
+ if ( psNdsSecurityContext->Credential ) {
+ FREE_POOL( psNdsSecurityContext->Credential );
+ psNdsSecurityContext->Credential = NULL;
+ }
+
+ if ( psNdsSecurityContext->Signature ) {
+ FREE_POOL( psNdsSecurityContext->Signature );
+ psNdsSecurityContext->Signature = NULL;
+ }
+
+ if ( psNdsSecurityContext->PublicNdsKey ) {
+ FREE_POOL( psNdsSecurityContext->PublicNdsKey );
+ psNdsSecurityContext->PublicNdsKey = NULL;
+ psNdsSecurityContext->PublicKeyLen = 0;
+ }
+
+ NwReleaseCredList( pUserLogon );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+BeginLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD userId,
+ OUT DWORD *loginId,
+ OUT DWORD *challenge
+)
+/*++
+
+Routine Desription:
+
+ Begin the NDS login process. The loginId returned is objectId of the user
+ on the server which created the account (may not be the current server).
+
+Arguments:
+
+ pIrpContext - The IRP context for this connection.
+ userId - The user's NDS object Id.
+ loginId - The objectId used to encrypt password.
+ challenge - The 4 byte random challenge.
+
+Return value:
+
+ NTSTATUS - The result of the operation.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Enter BeginLogin...\n", 0 );
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Announce myself.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_BEGIN_LOGIN,
+ &NdsRequest,
+ "DD",
+ 0,
+ userId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the object id and the challenge string.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_DD",
+ sizeof( DWORD ),
+ loginId,
+ challenge );
+
+ if ( NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Login 4 byte challenge: 0x%08lx\n", *challenge );
+ } else {
+ DebugTrace( 0, Dbg, "Begin login failed...\n", 0 );
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+FinishLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOID,
+ IN DWORD dwLoginFlags,
+ IN BYTE pbEncryptedChallenge[16],
+ IN BYTE *pbServerPublicBsafeKey,
+ IN int cbServerPublicBsafeKeyLen,
+ OUT BYTE *pbUserEncPrivateNdsKey,
+ OUT int *pcbUserEncPrivateNdsKeyLen,
+ OUT DWORD *pdwCredentialStartTime,
+ OUT DWORD *pdwCredentialEndTime
+)
+/*++
+
+Routine Description:
+
+ Constructs and sends the Finish Login request to the server.
+
+Arguments:
+
+ pIrpContext - (IN) IRP context for this request
+ dwUserOID - (IN) user's NDS object Id
+ pbEncryptedChallenge - (IN) RC2 encrypted challenge
+ pbServerPublicBsafeKey - (IN) server public bsafe key
+ cbServerPublicBsafeKeyLen - (IN) length of server public key
+
+ pbUserEncPrivateNdsKey - (OUT) user's encrypted private nds key
+ pcbUserEncPrivateNdsKeyLen - (OUT) length of pbUserEncPrivateNdsKey
+ pdwCredentialStartTime - (OUT) validity start time for credentials
+ pdwCredentialEndTime - (OUT) validity end time for credentials
+
+--*/
+{
+ NTSTATUS Status;
+
+ const int cbEncryptedChallengeLen = 16;
+
+ int LOG_DATA_POOL_SIZE, // pool sizes for our allocation call
+ PACKET_POOL_SIZE,
+ RESP_POOL_SIZE;
+
+ BYTE *pbRandomBytes; // random bytes used in crypto routines
+ BYTE RandRC2SecretKey[RC2_KEY_LEN]; // random RC2 key generated from above
+ BYTE pbEncryptedChallengeKey[RC2_KEY_LEN]; // RC2 key that will decode the response
+
+ NDS_RAND_BYTE_BLOCK *psRandByteBlock;
+
+ ENC_BLOCK_HDR *pbEncRandSeedHead; // header for encrypted random RC2 key seed
+ BYTE *pbEncRandSeed; // encrypted random seed
+ int cbPackedRandSeedLen; // length of the packed rand seed bytes
+
+ ENC_BLOCK_HDR *pbEncChallengeHead; // header for encrypted challenge
+
+ ENC_BLOCK_HDR *pbEncLogDataHead; // header for encrypted login data
+ BYTE *pbEncLogData; // encrypted login data
+ int cbEncLogDataLen; // length of the encrypted login data
+
+ ENC_BLOCK_HDR *pbEncServerRespHead; // header for encrypted response
+ BYTE *pbEncServerResp; // encrypted response
+
+ int CryptStatus, // crypt call status
+ CryptLen, // length of encrypted data
+ RequestPacketLen, // length of the request packet data
+ cbServerRespLen; // server response length after decryption
+
+ BYTE *pbServerResponse; // response from the server
+ DWORD cbEncServerRespLen; // server response length before decryption
+
+ DWORD EncKeyLen; // length of the encrypted private key
+ ENC_BLOCK_HDR *pbPrivKeyHead; // encryption header of the private key
+
+ LOCKED_BUFFER NdsRequest; // fragex locked buffer
+ BOOL PasswordExpired = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Enter FinishLogin...\n", 0 );
+
+ //
+ // Allocate working space. The login data pool starts at
+ // pbRandomBytes. The packet data starts at pbEncRandSeedHead.
+ // The server response pool starts at pbServerResponse.
+ //
+
+ //
+ // BUGBUG: The alignment of these structures may possibly be wrong on
+ // quad aligned machines; check out a hardware independent fix.
+ //
+
+ LOG_DATA_POOL_SIZE = RAND_KEY_DATA_LEN + // 28 bytes for random seed
+ sizeof ( NDS_RAND_BYTE_BLOCK ) + // login data random header
+ sizeof ( ENC_BLOCK_HDR ) + // header for encrypted challenge
+ cbEncryptedChallengeLen + // data for encrypted challenge
+ 8; // padding
+ LOG_DATA_POOL_SIZE = ROUNDUP4( LOG_DATA_POOL_SIZE );
+
+ PACKET_POOL_SIZE = 2048; // packet buffer size
+ RESP_POOL_SIZE = 2048; // packet buffer size
+
+ pbRandomBytes = ALLOCATE_POOL( PagedPool,
+ LOG_DATA_POOL_SIZE +
+ PACKET_POOL_SIZE +
+ RESP_POOL_SIZE );
+
+ if ( !pbRandomBytes ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in FinishLogin (main block)...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ pbEncRandSeedHead = ( PENC_BLOCK_HDR ) ( pbRandomBytes + LOG_DATA_POOL_SIZE );
+ pbServerResponse = ( pbRandomBytes + LOG_DATA_POOL_SIZE + PACKET_POOL_SIZE );
+
+ //
+ // Start working on the login data. As is common in the crypto world, we
+ // generate a random seed and then make a key from it to be used with a
+ // bulk cipher algorithm. In Netware land, we use MAC to make an 8 byte
+ // key from the random seed and use 64bit RC2 as our bulk cipher. We then
+ // RSA encrypt the seed using the server's public RSA key and use the bulk
+ // cipher to encrypt the rest of our login data.
+ //
+ // Since Novell uses 64bit RC2, the security isn't great.
+ //
+
+ GenRandomBytes( pbRandomBytes, RAND_KEY_DATA_LEN );
+ GenKey8( pbRandomBytes, RAND_KEY_DATA_LEN, RandRC2SecretKey );
+
+ //
+ // Now work on the actual packet data. Create the header for the
+ // encrypted random seed and pack the seed into it.
+ //
+
+ pbEncRandSeed = ( ( BYTE * )pbEncRandSeedHead ) + sizeof( ENC_BLOCK_HDR );
+
+ pbEncRandSeedHead->cipherLength = RSAGetInputBlockSize( pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen );
+
+ cbPackedRandSeedLen = RSAPack( pbRandomBytes,
+ RAND_KEY_DATA_LEN,
+ pbEncRandSeed,
+ pbEncRandSeedHead->cipherLength );
+ //
+ // We should have packed exactly one block.
+ //
+
+ if( cbPackedRandSeedLen != pbEncRandSeedHead->cipherLength ) {
+ DebugTrace( 0, Dbg, "RSAPack didn't pack exactly one block!\n", 0 );
+ }
+
+ pbEncRandSeedHead->cipherLength = RSAPublic( pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen,
+ pbEncRandSeed,
+ pbEncRandSeedHead->cipherLength,
+ pbEncRandSeed );
+
+ if ( !pbEncRandSeedHead->cipherLength ) {
+
+ DebugTrace( 0, Dbg, "Failing in FinishLogin... encryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Fill in the rest of the header for the random seed. We don't count
+ // the first DWORD in the EBH; it isn't part of the header as netware
+ // wants it, per se.
+ //
+
+ pbEncRandSeedHead->blockLength = pbEncRandSeedHead->cipherLength +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+ pbEncRandSeedHead->version = 1;
+ pbEncRandSeedHead->encType = ENC_TYPE_RSA_PUBLIC;
+ pbEncRandSeedHead->dataLength = RAND_KEY_DATA_LEN;
+
+ //
+ // Go back to working on the login data. Fill out the rbb.
+ //
+
+ psRandByteBlock = ( PNDS_RAND_BYTE_BLOCK ) ( pbRandomBytes + RAND_KEY_DATA_LEN );
+
+ GenRandomBytes( (BYTE *) &psRandByteBlock->rand1, 4 );
+ psRandByteBlock->rand2Len = RAND_FL_DATA_LEN;
+ GenRandomBytes( (BYTE *) &psRandByteBlock->rand2[0], RAND_FL_DATA_LEN );
+
+ //
+ // Fill out the header for the encrypted challenge right after the rbb.
+ //
+
+ pbEncChallengeHead = (ENC_BLOCK_HDR *) ( ((BYTE *)psRandByteBlock) +
+ sizeof( NDS_RAND_BYTE_BLOCK ) );
+
+ pbEncChallengeHead->version = 1;
+ pbEncChallengeHead->encType = ENC_TYPE_RC2_CBC;
+ pbEncChallengeHead->dataLength = 4;
+ pbEncChallengeHead->cipherLength = cbEncryptedChallengeLen;
+ pbEncChallengeHead->blockLength = cbEncryptedChallengeLen +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+
+ //
+ // Place the encrypted challenge immediately after its header.
+ //
+
+ RtlCopyMemory( (BYTE *)( ((BYTE *)pbEncChallengeHead) +
+ sizeof( ENC_BLOCK_HDR )),
+ pbEncryptedChallenge,
+ cbEncryptedChallengeLen );
+
+ //
+ // Prepare the RC2 key to decrypt FinishLogin response.
+ //
+
+ GenKey8( (BYTE *)( &pbEncChallengeHead->version ),
+ pbEncChallengeHead->blockLength,
+ pbEncryptedChallengeKey );
+
+ //
+ // Finish up the packet data by preparing the login data. Start
+ // with the encryption header.
+ //
+
+ pbEncLogDataHead = ( PENC_BLOCK_HDR ) ( pbEncRandSeed +
+ ROUNDUP4( pbEncRandSeedHead->cipherLength ) );
+
+ pbEncLogData = ( ( BYTE * )pbEncLogDataHead ) + sizeof( ENC_BLOCK_HDR );
+
+ pbEncLogDataHead->version = 1;
+ pbEncLogDataHead->encType = ENC_TYPE_RC2_CBC;
+ pbEncLogDataHead->dataLength = sizeof( NDS_RAND_BYTE_BLOCK ) +
+ sizeof( ENC_BLOCK_HDR ) +
+ cbEncryptedChallengeLen;
+
+ //
+ // Sanity check the packet pool for overflow.
+ //
+
+ if ( ( pbEncLogData + pbEncLogDataHead->dataLength + ( 2 * CIPHERBLOCKSIZE ) ) -
+ (BYTE *) pbEncRandSeedHead > PACKET_POOL_SIZE ) {
+
+ DebugTrace( 0, Dbg, "Packet pool overflow... I'd better fix this.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Encrypt the login data.
+ //
+
+ CryptStatus = CBCEncrypt( RandRC2SecretKey,
+ NULL,
+ (BYTE *)psRandByteBlock,
+ pbEncLogDataHead->dataLength,
+ pbEncLogData,
+ &CryptLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "Encryption failure in FinishLogin...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ pbEncLogDataHead->cipherLength = (WORD)CryptLen;
+ pbEncLogDataHead->blockLength = pbEncLogDataHead->cipherLength +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+
+ //
+ // We can finally send out the finish login request! Calculate the
+ // send amount and make the request.
+ //
+
+ RequestPacketLen = ( (BYTE *) pbEncLogData + pbEncLogDataHead->cipherLength ) -
+ (BYTE *) pbEncRandSeedHead;
+
+ NdsRequest.pRecvBufferVa = pbServerResponse;
+ NdsRequest.dwRecvLen = RESP_POOL_SIZE;
+ NdsRequest.pRecvMdl = NULL;
+
+ NdsRequest.pRecvMdl = ALLOCATE_MDL( pbServerResponse,
+ RESP_POOL_SIZE,
+ FALSE,
+ FALSE,
+ NULL );
+ if ( !NdsRequest.pRecvMdl ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ MmProbeAndLockPages( NdsRequest.pRecvMdl,
+ KernelMode,
+ IoWriteAccess );
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_FINISH_LOGIN,
+ &NdsRequest,
+ "DDDDDDDr",
+ 2, // Version
+ dwLoginFlags, // Flags
+ dwUserOID, // Entry ID
+ 0x494, //
+ 1, // Security Version
+ 0x20009, // Envelope ID 1
+ 0x488, // Envelope length
+ pbEncRandSeedHead, // Cipher block
+ RequestPacketLen ); // Cipher block length
+
+ MmUnlockPages( NdsRequest.pRecvMdl );
+ FREE_MDL( NdsRequest.pRecvMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ cbServerRespLen = NdsRequest.dwBytesWritten;
+
+ //
+ // Save the credential validity times.
+ //
+
+ Status = ParseResponse( NULL,
+ pbServerResponse,
+ cbServerRespLen,
+ "G_DD",
+ sizeof( DWORD ),
+ pdwCredentialStartTime,
+ pdwCredentialEndTime );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Grab the encryption block header for the response. This response in
+ // RC2 encrypted with the pbEncryptedChallengeKey.
+ //
+
+ pbEncServerRespHead = (ENC_BLOCK_HDR *) ( pbServerResponse +
+ ( 3 * sizeof( DWORD ) ) );
+
+ if ( pbEncServerRespHead->encType != ENC_TYPE_RC2_CBC ||
+ pbEncServerRespHead->cipherLength >
+ ( RESP_POOL_SIZE + sizeof( ENC_BLOCK_HDR ) + 12 ) ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Decrypt the server response in place.
+ //
+
+ pbEncServerResp = ( BYTE * ) ( ( BYTE * ) pbEncServerRespHead +
+ sizeof( ENC_BLOCK_HDR ) );
+
+ CryptStatus = CBCDecrypt( pbEncryptedChallengeKey,
+ NULL,
+ pbEncServerResp,
+ pbEncServerRespHead->cipherLength,
+ pbEncServerResp,
+ &cbServerRespLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ||
+ cbServerRespLen != pbEncServerRespHead->dataLength ) {
+
+ DebugTrace( 0, Dbg, "Encryption failure (2) in FinishLogin...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Examine the first random number to make sure the server is authentic.
+ //
+
+ if ( psRandByteBlock->rand1 != * ( DWORD * ) pbEncServerResp ) {
+
+ DebugTrace( 0, Dbg, "Server failed to respond to our challenge correctly...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // We know things are legit, so we can extract the private key.
+ // Careful, though: don't XOR the size dword.
+ //
+
+ pbEncServerResp += sizeof( DWORD );
+ EncKeyLen = * ( DWORD * ) ( pbEncServerResp );
+
+ pbEncServerResp += sizeof( DWORD );
+ while ( EncKeyLen-- ) {
+
+ pbEncServerResp[EncKeyLen] ^= psRandByteBlock->rand2[EncKeyLen];
+ }
+
+ //
+ // Check the encryption header on the private key. Don't forget to
+ // backup to include the size dword.
+ //
+
+ pbPrivKeyHead = ( ENC_BLOCK_HDR * )( pbEncServerResp - sizeof( DWORD ) ) ;
+
+ if ( pbPrivKeyHead->encType != ENC_TYPE_RC2_CBC ) {
+
+ DebugTrace( 0, Dbg, "Bad encryption header on the private key...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Finally, copy out the user's private NDS key.
+ //
+
+ if ( *pcbUserEncPrivateNdsKeyLen >= pbPrivKeyHead->cipherLength ) {
+
+ DebugTrace( 0, Dbg, "Encrypted private key len: %d\n",
+ pbPrivKeyHead->cipherLength );
+
+ RtlCopyMemory( pbUserEncPrivateNdsKey,
+ ((BYTE *)( pbPrivKeyHead )) + sizeof( ENC_BLOCK_HDR ),
+ pbPrivKeyHead->cipherLength );
+
+ *pcbUserEncPrivateNdsKeyLen = pbPrivKeyHead->cipherLength;
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Encryption failure on private key in FinishLogin...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( pbRandomBytes );
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( PasswordExpired ) ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+ChangeNdsPassword(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOID,
+ DWORD dwChallenge,
+ PBYTE pbOldPwHash,
+ PBYTE pbNewPwHash,
+ PNDS_PRIVATE_KEY pUserPrivKey,
+ PBYTE pServerPublicBsafeKey,
+ UINT ServerPubKeyLen
+)
+/*+++
+
+Description:
+
+ Send a change password packet. Change the users password
+ on the NDS tree that this irp context points to.
+
+Arguments:
+
+ pIrpContext - The irp context for this request. Points to the target server.
+ dwUserOID - The oid for the current user.
+ dwChallenge - The encrypted challenge from begin login.
+ pbOldPwHash - The 16 byte hash of the old password.
+ pbNewPwHash - The 16 byte hash of the new password.
+ pUserPrivKey - The user's private RSA key with NDS header.
+ pServerPublicBsafeKey - The server's public RSA key in BSAFE format.
+ ServerPubKeyLen - The length of the server's public BSAFE key.
+
+--*/
+{
+ NTSTATUS Status;
+ BYTE pbNewPwKey[8];
+ BYTE pbSecretKey[8];
+ PENC_BLOCK_HDR pbEncSecretKey, pbEncChangePassReq;
+ BYTE RandomBytes[RAND_KEY_DATA_LEN];
+ PBYTE pbEncData;
+ PNDS_CHPW_MSG pChangePassMsg;
+ INT CryptStatus, CryptLen;
+ DWORD dwTotalEncDataLen;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Create a secret key from the new password.
+ //
+
+ GenKey8( pbNewPwHash, 16, pbNewPwKey );
+
+ pbEncSecretKey = ALLOCATE_POOL( PagedPool,
+ ( ( 2 * sizeof( ENC_BLOCK_HDR ) ) +
+ ( MAX_RSA_BYTES ) +
+ ( sizeof( NDS_CHPW_MSG ) ) +
+ ( sizeof( NDS_PRIVATE_KEY ) ) +
+ ( pUserPrivKey->keyDataLength ) +
+ 16 ) );
+
+ if ( !pbEncSecretKey ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: Out of memory.\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( pbEncSecretKey );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Generate a random key.
+ //
+
+ GenRandomBytes( RandomBytes, RAND_KEY_DATA_LEN );
+ GenKey8( RandomBytes, RAND_KEY_DATA_LEN, pbSecretKey );
+
+ //
+ // Encrypt the secret key data in the space after the EBH.
+ //
+
+ pbEncSecretKey->dataLength = RAND_KEY_DATA_LEN;
+ pbEncSecretKey->cipherLength = RSAGetInputBlockSize( pServerPublicBsafeKey, ServerPubKeyLen);
+
+ pbEncData = ( PBYTE ) ( pbEncSecretKey + 1 );
+
+ pbEncSecretKey->cipherLength = RSAPack( RandomBytes,
+ pbEncSecretKey->dataLength,
+ pbEncData,
+ pbEncSecretKey->cipherLength );
+
+ pbEncSecretKey->cipherLength = RSAPublic( pServerPublicBsafeKey,
+ ServerPubKeyLen,
+ pbEncData,
+ pbEncSecretKey->cipherLength,
+ pbEncData );
+
+ if ( !pbEncSecretKey->cipherLength ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: RSA encryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Finish filling out the EBH for the secret key block.
+ //
+
+ pbEncSecretKey->version = 1;
+ pbEncSecretKey->encType = ENC_TYPE_RSA_PUBLIC;
+ pbEncSecretKey->blockLength = pbEncSecretKey->cipherLength +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+
+ //
+ // Now form the change password request.
+ //
+
+ pbEncChangePassReq = ( PENC_BLOCK_HDR )
+ ( pbEncData + ROUNDUP4( pbEncSecretKey->cipherLength ) );
+
+ pChangePassMsg = ( PNDS_CHPW_MSG ) ( pbEncChangePassReq + 1 );
+
+ //
+ // Init the Change Password message.
+ //
+
+ pChangePassMsg->challenge = dwChallenge;
+ pChangePassMsg->oldPwLength = pChangePassMsg->newPwLength = 16;
+
+ RtlCopyMemory( pChangePassMsg->oldPwHash, pbOldPwHash, pChangePassMsg->oldPwLength );
+ RtlCopyMemory( pChangePassMsg->newPwHash, pbNewPwHash, pChangePassMsg->newPwLength );
+
+ pChangePassMsg->unknown = 8;
+ pChangePassMsg->encPrivKeyHdr.version = 1;
+ pChangePassMsg->encPrivKeyHdr.encType = ENC_TYPE_RC2_CBC;
+ pChangePassMsg->encPrivKeyHdr.dataLength = pUserPrivKey->keyDataLength + sizeof( NDS_PRIVATE_KEY );
+
+ //
+ // Encrypt the private key with the key derived from the new password.
+ //
+
+ CryptStatus = CBCEncrypt( pbNewPwKey,
+ NULL,
+ ( PBYTE ) pUserPrivKey,
+ pChangePassMsg->encPrivKeyHdr.dataLength,
+ ( PBYTE ) ( pChangePassMsg + 1 ),
+ &CryptLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: CBC encrypt failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Finish filling out the encryption header.
+ //
+
+ pChangePassMsg->encPrivKeyHdr.cipherLength = CryptLen;
+ pChangePassMsg->encPrivKeyHdr.blockLength = CryptLen +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+ pbEncChangePassReq->version = 1;
+ pbEncChangePassReq->encType = ENC_TYPE_RC2_CBC;
+ pbEncChangePassReq->dataLength = sizeof( NDS_CHPW_MSG ) + CryptLen;
+
+ //
+ // Encrypt the whole Change Password message in-place with the secret key.
+ //
+
+ CryptStatus = CBCEncrypt( pbSecretKey,
+ NULL,
+ ( PBYTE ) pChangePassMsg,
+ pbEncChangePassReq->dataLength,
+ ( PBYTE ) pChangePassMsg,
+ &CryptLen,
+ BSAFE_CHECKSUM_LEN);
+
+ if ( CryptStatus ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: Second CBC encrypt failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ pbEncChangePassReq->cipherLength = CryptLen;
+ pbEncChangePassReq->blockLength =
+ CryptLen + sizeof( ENC_BLOCK_HDR ) - sizeof( DWORD );
+
+ //
+ // Calculate the size of the request.
+ //
+
+ dwTotalEncDataLen = sizeof( ENC_BLOCK_HDR ) + // Secret key header.
+ ROUNDUP4( pbEncSecretKey->cipherLength ) + // Secret key data.
+ sizeof( ENC_BLOCK_HDR ) + // Change pass msg header.
+ CryptLen; // Change pass data.
+
+ //
+ // Send this change password message to the server.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_CHANGE_PASSWORD,
+ &NdsRequest,
+ "DDDDDDr",
+ 0,
+ dwUserOID,
+ dwTotalEncDataLen + ( 3 * sizeof( DWORD ) ),
+ 1,
+ 0x20009,
+ dwTotalEncDataLen,
+ pbEncSecretKey,
+ dwTotalEncDataLen );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( pbEncSecretKey );
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsServerAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNDS_SECURITY_CONTEXT pNdsContext
+)
+/*++
+
+Routine Description:
+
+ Authenticate an NDS connection.
+ The user must have already logged into the NDS tree.
+
+ If you change this function - know that you cannot
+ at any point try to acquire the nds credential
+ resource exclusive from here since that could cause
+ a dead lock!!!
+
+ You also must not dequeue the irp context!
+
+Arguments:
+
+ pIrpContext - IrpContext for the server that we want to authenticate to.
+
+Return value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+
+ BYTE *pbUserPublicBsafeKey;
+ int cbUserPublicBsafeKeyLen;
+
+ NDS_AUTH_MSG *psAuthMsg;
+ NDS_CREDENTIAL *psLocalCredential;
+ DWORD dwLocalCredentialLen;
+ UNICODE_STRING uUserName;
+ DWORD UserOID;
+
+ BYTE *x, *y, *r;
+ BYTE CredentialHash[16];
+ int i, rsaBlockSize, rsaModSize, totalXLen;
+ DWORD dwServerRand;
+
+ BYTE *pbResponse;
+ DWORD cbResponseLen;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Entering NdsServerAuthenticate...\n", 0 );
+
+ //
+ // Allocate space for the auth msg, credential, G-Q bytes, and
+ // the response buffer.
+ //
+
+ psAuthMsg = ALLOCATE_POOL( PagedPool,
+ sizeof( NDS_AUTH_MSG ) + // auth message
+ sizeof( NDS_CREDENTIAL ) + // credential
+ MAX_NDS_NAME_SIZE + //
+ ( MAX_RSA_BYTES * 9 ) ); // G-Q rands
+
+ if ( !psAuthMsg ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsServerAuthenticate (0)...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pbResponse = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !pbResponse ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsServerAuthenticate (1)...\n", 0 );
+ FREE_POOL( psAuthMsg );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ psLocalCredential = (PNDS_CREDENTIAL)( ((BYTE *) psAuthMsg) +
+ sizeof( NDS_AUTH_MSG ) );
+
+ //
+ // Locate the public BSAFE key.
+ //
+
+ cbUserPublicBsafeKeyLen = NdsGetBsafeKey ( (BYTE *)(pNdsContext->PublicNdsKey),
+ pNdsContext->PublicKeyLen,
+ &pbUserPublicBsafeKey );
+
+ //
+ // Get the user's object Id but do not jump dir servers. There is never
+ // any optional data, so we don't really need to skip over it.
+ //
+
+ uUserName.MaximumLength = pNdsContext->Credential->userNameLength;
+ uUserName.Length = uUserName.MaximumLength;
+ uUserName.Buffer = ( WCHAR * ) ( ((BYTE *)pNdsContext->Credential) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ &uUserName,
+ &UserOID,
+ FALSE,
+ RSLV_DEREF_ALIASES | RSLV_CREATE_ID | RSLV_ENTRY_ID );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Issue the Begin Authenticate request to get the random server nonce.
+ //
+
+ Status = BeginAuthenticate( pIrpContext,
+ UserOID,
+ &dwServerRand );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Figure out the size of the zero-padded RSA Blocks. We use the same
+ // size as the modulus field of the public key (typically 56 bytes).
+ //
+
+ RSAGetModulus( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ &rsaBlockSize);
+
+ DebugTrace( 0, Dbg, "RSA block size for authentication: %d\n", rsaBlockSize );
+
+ //
+ // Prepare the credential and the 3 G-Q rands. The credential,
+ // xs, and ys go out in the packet; rs is secret.
+ //
+
+ RtlZeroMemory( ( BYTE * )psLocalCredential,
+ sizeof( NDS_CREDENTIAL ) +
+ MAX_NDS_NAME_SIZE +
+ 9 * rsaBlockSize );
+
+ dwLocalCredentialLen = sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize +
+ pNdsContext->Credential->userNameLength;
+
+ DebugTrace( 0, Dbg, "Credential length is %d.\n", dwLocalCredentialLen );
+
+ RtlCopyMemory( (BYTE *)psLocalCredential,
+ pNdsContext->Credential,
+ dwLocalCredentialLen );
+
+ x = ( BYTE * ) psAuthMsg + sizeof( NDS_AUTH_MSG ) + dwLocalCredentialLen;
+ y = x + ( 3 * rsaBlockSize );
+ r = y + ( 3 * rsaBlockSize );
+
+ rsaModSize = RSAGetInputBlockSize( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen );
+
+ DebugTrace( 0, Dbg, "RSA modulus size: %d\n", rsaModSize );
+
+ for ( i = 0; i < 3; i++ ) {
+
+ //
+ // Create Random numbers r1, r2 and r3 of modulus size.
+ //
+
+ GenRandomBytes( r + ( rsaBlockSize * i ), rsaModSize );
+
+ //
+ // Compute x = r**e mod N.
+ //
+
+ RSAPublic( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ r + ( rsaBlockSize * i ),
+ rsaModSize,
+ x + ( rsaBlockSize * i ) );
+
+ }
+
+ //
+ // Fill in the AuthMsg fields.
+ //
+
+ psAuthMsg->version = 0;
+ psAuthMsg->svrRand = dwServerRand;
+ psAuthMsg->verb = NDSV_FINISH_AUTHENTICATE;
+ psAuthMsg->credentialLength = dwLocalCredentialLen;
+
+ //
+ // MD2 hash the auth message, credential and x's.
+ //
+
+ MD2( (BYTE *)psAuthMsg,
+ sizeof( NDS_AUTH_MSG ) +
+ psAuthMsg->credentialLength +
+ ( 3 * rsaBlockSize ),
+ CredentialHash );
+
+ //
+ // Compute yi = ri*(S**ci) mod N; c1,c2,c3 are the first three
+ // 16 bit numbers in CredentialHash.
+ //
+
+ totalXLen = 3 * rsaBlockSize;
+
+ for ( i = 0; i < 3; i++ ) {
+
+ RSAModExp( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ ( (BYTE *)(pNdsContext->Signature) ) + sizeof( NDS_SIGNATURE ),
+ pNdsContext->Signature->signDataLength,
+ &CredentialHash[i * sizeof( WORD )],
+ sizeof( WORD ),
+ y + ( rsaBlockSize * i) );
+
+ RSAModMpy( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ y + ( rsaBlockSize * i ), // input1 = S**ci mod N
+ rsaModSize + 1,
+ r + ( rsaBlockSize * i ), // input2 = ri
+ rsaModSize,
+ y + ( rsaBlockSize * i ) ); // output = yi
+ }
+
+ //
+ // Send the auth proof.
+ //
+
+ NdsRequest.pRecvBufferVa = pbResponse;
+ NdsRequest.dwRecvLen = NDS_BUFFER_SIZE;
+ NdsRequest.pRecvMdl = NULL;
+
+ NdsRequest.pRecvMdl = ALLOCATE_MDL( pbResponse,
+ NDS_BUFFER_SIZE,
+ FALSE,
+ FALSE,
+ NULL );
+ if ( !NdsRequest.pRecvMdl ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ MmProbeAndLockPages( NdsRequest.pRecvMdl,
+ KernelMode,
+ IoWriteAccess );
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_FINISH_AUTHENTICATE,
+ &NdsRequest,
+ "DDDrDDWWWWr",
+ 0, // version
+ 0, // sessionKeyLen
+ psAuthMsg->credentialLength, // credential len
+ (BYTE *)psLocalCredential, // actual credential
+ ROUNDUP4( psAuthMsg->credentialLength ),
+ 12 + ( totalXLen * 2 ), // length of remaining
+ 1, // proof version?
+ 8, // tag?
+ 16, // message digest base
+ 3, // proof order
+ totalXLen, // proofOrder*sizeof(x)
+ x, // x1,x2,x3,y1,y2,y3
+ 2 * totalXLen );
+
+ MmUnlockPages( NdsRequest.pRecvMdl );
+ FREE_MDL( NdsRequest.pRecvMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ cbResponseLen = NdsRequest.dwBytesWritten;
+ DebugTrace( 0, Dbg, "Authentication returned ok status.\n", 0 );
+
+ //
+ // We completed NDS authentication, so clear out the name
+ // and password in the SCB so that we use the credentials
+ // from now on.
+ //
+
+ if ( pIrpContext->pScb->UserName.Buffer != NULL ) {
+
+ DebugTrace( 0, Dbg, "Clearing out bindery login data.\n", 0 );
+
+ pIrpContext->pScb->UserName.Length = 0;
+ pIrpContext->pScb->UserName.MaximumLength = 0;
+
+ pIrpContext->pScb->Password.Length = 0;
+ pIrpContext->pScb->Password.MaximumLength = 0;
+
+ FREE_POOL( pIrpContext->pScb->UserName.Buffer );
+ RtlInitUnicodeString( &pIrpContext->pScb->UserName, NULL );
+ RtlInitUnicodeString( &pIrpContext->pScb->Password, NULL );
+
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( psAuthMsg );
+ FREE_POOL( pbResponse );
+
+ return Status;
+}
+
+NTSTATUS
+BeginAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserId,
+ OUT DWORD *pdwSvrRandom
+)
+/*++
+
+Routine Description:
+
+ Authenticate an NDS connection.
+ The user must have already logged into the NDS tree.
+
+Arguments:
+
+ pIrpContext - IrpContext for the server that we want to authenticate to.
+ dwUserID - The user OID that we are authenticating ourselves as.
+ pdwSvrRandon - The server random challenge.
+
+Return value:
+
+ NTSTATUS - The result of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ DWORD dwClientRand;
+
+ PAGED_CODE();
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ GenRandomBytes( (BYTE *)&dwClientRand, sizeof( dwClientRand ) );
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_BEGIN_AUTHENTICATE,
+ &NdsRequest,
+ "DDD",
+ 0, // Version.
+ dwUserId, // Entry Id.
+ dwClientRand ); // Client's random challenge.
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ goto ExitWithCleanup;
+ }
+
+ //
+ // The reply actually contains all this, even though we don't look at it?
+ //
+ // typedef struct {
+ // DWORD svrRand;
+ // DWORD totalLength;
+ // TAG_DATA_HEADER tdh;
+ // WORD unknown; // Always 2.
+ // DWORD encClientRandLength;
+ // CIPHER_BLOCK_HEADER keyCipherHdr;
+ // BYTE keyCipher[];
+ // CIPHER_BLOCK_HEADER encClientRandHdr;
+ // BYTE encClientRand[];
+ // } REPLY_BEGIN_AUTHENTICATE;
+ //
+ // Nah, that can't be right.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_D",
+ sizeof( DWORD ),
+ pdwSvrRandom );
+
+ //
+ // We either got it or we didn't.
+ //
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+}
+
+NTSTATUS
+NdsLicenseConnection(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send the license NCP to the server to license this connection.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Licensing connection to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+
+ //
+ // Change the authentication state of the connection.
+ //
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "SD",
+ NCP_ADMIN_FUNCTION,
+ NCP_CHANGE_CONN_AUTH_STATUS,
+ NCP_CONN_LICENSED );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Licensing failed to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsUnlicenseConnection(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send the license NCP to the server to license this connection.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Unlicensing connection to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+
+ //
+ // Change the authentication state of the connection.
+ //
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "SD",
+ NCP_ADMIN_FUNCTION,
+ NCP_CHANGE_CONN_AUTH_STATUS,
+ NCP_CONN_NOT_LICENSED );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Unlicensing failed to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+ }
+
+ return Status;
+}
+
+int
+NdsGetBsafeKey(
+ UCHAR *pPubKey,
+ const int pubKeyLen,
+ UCHAR **ppBsafeKey
+)
+/*++
+
+Routine Description:
+
+ Locate the BSAFE key from within the public key. Note that this does
+ not work for private keys in NDS format. For private keys, you just
+ skip the size word.
+
+ This is verbatim from Win95.
+
+Routine Arguments:
+
+ pPubKey - A pointer to the public key.
+ pubKeyLen - The length of the public key.
+ ppBsafeKey - The pointer to the BSAFE key in the public key.
+
+Return Value:
+
+ The length of the BSAFE key.
+
+--*/
+{
+ int bsafePubKeyLen = 0, totalDNLen;
+ char *pRcv;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ totalDNLen = 0;
+ Status = ParseResponse( NULL,
+ pPubKey,
+ pubKeyLen,
+ "G_W",
+ ( 2 * sizeof( DWORD ) ) + sizeof( WORD ),
+ &totalDNLen );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Exit;
+ }
+
+ Status = ParseResponse( NULL,
+ pPubKey,
+ pubKeyLen - 12,
+ "G__W",
+ 12,
+ 5 * sizeof( WORD ) +
+ 3 * sizeof( DWORD ) +
+ totalDNLen,
+ &bsafePubKeyLen );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Exit;
+ }
+
+ *ppBsafeKey = (UCHAR *) pPubKey +
+ 14 +
+ 5 * sizeof( WORD ) +
+ 3 * sizeof( DWORD ) +
+ totalDNLen;
+
+
+Exit:
+
+ return bsafePubKeyLen;
+}
+
+NTSTATUS
+NdsLogoff(
+ IN PIRP_CONTEXT pIrpContext
+)
+/*++
+
+Routine Description:
+
+ Sends a logout to the NDS tree, closes all NDS authenticated
+ connections, and destroys the current set of NDS credentials.
+
+ This routine acquires the credential list exclusive.
+
+Arguments:
+
+ pIrpContext - The IRP context for this request pointed to a
+ valid dir server.
+
+Notes:
+
+ This is only called from DeleteConnection. The caller owns
+ the RCB exclusive and we will free it before returning.
+
+--*/
+{
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+ PNDS_SECURITY_CONTEXT pCredentials;
+ PLOGON pLogon;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNextNpScb;
+ KIRQL OldIrql;
+
+ //
+ // Grab the user's LOGON structure.
+ //
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ //
+ // The caller owns the RCB.
+ //
+
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "Invalid security context for NdsLogoff.\n", 0 );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_NO_SUCH_USER;
+ }
+
+ //
+ // Check to make sure that we have something to log off from.
+ //
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_WRITE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status )) {
+ DebugTrace( 0, Dbg, "NdsLogoff: Nothing to log off from.\n", 0 );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+ //
+ // If the credentials are locked, then someone is already
+ // doing a logout.
+ //
+
+ if ( pCredentials->CredentialLocked ) {
+ DebugTrace( 0, Dbg, "NdsLogoff: Logoff already in progress.\n", 0 );
+ NwReleaseCredList( pLogon );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_DEVICE_BUSY;
+ }
+
+ //
+ // Mark the credential locked so we can logout without
+ // worrying about others logging in.
+ //
+
+ pCredentials->CredentialLocked = TRUE;
+
+ //
+ // Release all our resoures so we can jump around servers.
+ //
+
+ NwReleaseCredList( pLogon );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Look through the scb list for connections that are in use. If all
+ // existing connections can be closed down, then we can complete the logout.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ while ( pNextNpScb != pNpScb ) {
+
+ if ( pNextNpScb->pScb != NULL ) {
+
+ //
+ // Is this connection in use by us and is it NDS authenticated?
+ //
+
+ if ( RtlEqualUnicodeString( &pScb->NdsTreeName,
+ &pNextNpScb->pScb->NdsTreeName,
+ TRUE ) &&
+ ( pScb->UserUid.QuadPart == pNextNpScb->pScb->UserUid.QuadPart ) &&
+ ( pNextNpScb->State == SCB_STATE_IN_USE ) &&
+ ( pNextNpScb->pScb->UserName.Length == 0 ) ) {
+
+ pIrpContext->pNpScb = pNextNpScb;
+ pIrpContext->pScb = pNextNpScb->pScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ if ( pNextNpScb->pScb->OpenFileCount == 0 ) {
+
+ //
+ // Can we close it anyway? Should we check
+ // for open handles and the such here?
+ //
+
+ pNextNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "NdsLogoff: Other connections in use.\n", 0 );
+
+ NwAcquireExclusiveCredList( pLogon );
+ pCredentials->CredentialLocked = FALSE;
+ NwReleaseCredList( pLogon );
+
+ NwDereferenceScb( pNextNpScb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ return STATUS_CONNECTION_IN_USE;
+
+ }
+ }
+
+ }
+
+ //
+ // Select the next scb.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ NwDereferenceScb( pNextNpScb );
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ }
+
+ NwDereferenceScb( pNpScb );
+
+ //
+ // The seed scb for the tree logout should be valid.
+ //
+
+ ASSERT( pNpScb->State == SCB_STATE_IN_USE );
+
+ //
+ // Check to make sure we can close the host scb.
+ //
+
+ if ( pScb->OpenFileCount != 0 ) {
+
+ DebugTrace( 0, Dbg, "NdsLogoff: Seed connection in use.\n", 0 );
+
+ NwAcquireExclusiveCredList( pLogon );
+ pCredentials->CredentialLocked = FALSE;
+ NwReleaseCredList( pLogon );
+
+ return STATUS_CONNECTION_IN_USE;
+ }
+
+ //
+ // We can actually do the logout, so remove the credentials from
+ // the resource list, release the resource, and logout.
+ //
+ // If we are deleting the preferred tree credentials,
+ // then we need to clear the preferred server.
+ //
+
+ if ( (pLogon->NdsCredentialList).Flink == &(pCredentials->Next) ) {
+
+ if ( pLogon->ServerName.Buffer != NULL ) {
+
+ DebugTrace( 0, Dbg, "Clearing preferred server at logout time.\n", 0 );
+
+ FREE_POOL( pLogon->ServerName.Buffer );
+ pLogon->ServerName.Length = pLogon->ServerName.MaximumLength = 0;
+ pLogon->ServerName.Buffer = NULL;
+
+ }
+ }
+
+ NwAcquireExclusiveCredList( pLogon );
+ RemoveEntryList( &pCredentials->Next );
+ NwReleaseCredList( pLogon );
+
+ FreeNdsContext( pCredentials );
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_LOGOUT,
+ &NdsRequest,
+ NULL );
+
+ NdsFreeLockedBuffer( &NdsRequest );
+
+ }
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ ASSERT( pScb->UserName.Buffer == NULL );
+ pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ return STATUS_SUCCESS;
+
+}
+
diff --git a/private/nw/rdr/ndsprocs.h b/private/nw/rdr/ndsprocs.h
new file mode 100644
index 000000000..87c3765b0
--- /dev/null
+++ b/private/nw/rdr/ndsprocs.h
@@ -0,0 +1,822 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsProcs.h
+
+Abstract:
+
+ This defines the necessary NDS data structures and
+ symbolic constants.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+Revision History:
+
+--*/
+
+#include "data.h"
+#include "nodetype.h"
+#include "struct.h"
+#include <stdarg.h>
+
+#include "crypto.h"
+
+//
+// Security information.
+//
+
+#define ENC_TYPE_RSA_PUBLIC 0x90001
+#define ENC_TYPE_RC2_CBC 0x60001
+
+#define RAND_KEY_DATA_LEN 28
+#define RAND_FL_DATA_LEN 1024
+#define RC2_KEY_LEN 8
+
+#define MAX_PUBLIC_KEY_LEN 1300
+#define MAX_BSAFE_PUBLIC_KEY_LEN 200 // Typically 179.
+#define MAX_BSAFE_PRIV_KEY_LEN 280 // Typically 273.
+
+#define MAX_PW_CHARS 16
+
+//
+// The max size for various NDS components.
+//
+
+#define MAX_RSA_BITS 512 // Really 420.
+
+#define NDS_TREE_NAME_LEN 32
+#define NDS_BINDERY_TREE_NAME 48
+
+#define MAX_CREDENTIAL_LEN ( sizeof( NDS_CREDENTIAL ) + MAX_NDS_NAME_SIZE )
+#define MAX_SIGNATURE_LEN ( sizeof( NDS_SIGNATURE ) + MAX_RSA_BYTES )
+#define MAX_ENC_PRIV_KEY_LEN ( MAX_BSAFE_PRIV_KEY_LEN + 64 )
+
+#define BSAFE_CHECKSUM_LEN 5
+
+#define DEFAULT_RESOLVE_FLAGS RSLV_DEREF_ALIASES | RSLV_WALK_TREE | RSLV_WRITABLE
+
+#include <packon.h>
+
+typedef struct {
+
+ DWORD syntaxId; // OCTET STRING (9)
+
+ struct {
+ DWORD nameLength;
+ WORD name[11]; // "Public Key"
+ WORD filler;
+ } attribName;
+
+ DWORD entries; // = 1
+ DWORD totalLength; // of attribute value OCTET STRING
+ DWORD unknown1; // = 1
+ DWORD unknown2; // = 4
+ WORD _issuerDNLength;
+ WORD totalDNLength;
+ WORD length2;
+ WORD length3;
+ WORD issuerDNLength;
+ WORD userDNLength;
+ WORD bsafeSectionLength;
+ DWORD length4;
+
+} PUBLIC_KEY_ATTRIB;
+
+#include <packoff.h>
+
+typedef struct {
+
+ DWORD blockLength; // cipherLength + size of following hdr fields
+ DWORD version; // = 1
+ DWORD encType; // 0x060001 for RC2; 0x090001 and 0x0A0001 for RSA
+ WORD cipherLength; // of ciphertext
+ WORD dataLength; // of plaintext
+
+} ENC_BLOCK_HDR, *PENC_BLOCK_HDR;
+
+typedef struct {
+
+ DWORD rand1;
+ DWORD rand2Len;
+ BYTE rand2[RAND_FL_DATA_LEN];
+
+} NDS_RAND_BYTE_BLOCK, *PNDS_RAND_BYTE_BLOCK;
+
+typedef struct {
+
+ DWORD version;
+ DWORD verb;
+ DWORD svrRand;
+ DWORD credentialLength;
+
+} NDS_AUTH_MSG, *PNDS_AUTH_MSG;
+
+//
+// VLM Uses the Tagged Data Store as a sort of registry on the fly.
+// We, of course, don't use it, but still need the headers.
+//
+// We need these to be packed.
+//
+
+#include <packon.h>
+
+typedef struct {
+ DWORD version;
+ WORD tag;
+} TAG_DATA_HEADER;
+
+#define TAG_PRIVATE_KEY 2
+#define TAG_PUBLIC_KEY 4
+#define TAG_CREDENTIAL 6
+#define TAG_SIGNATURE 7
+#define TAG_PROOF 8
+
+typedef struct {
+
+ TAG_DATA_HEADER tdh;
+ DWORD validityBegin;
+ DWORD validityEnd;
+ DWORD random;
+ WORD optDataSize;
+ WORD userNameLength;
+
+ // BYTE optData[optDataSize];
+ // BYTE userName[userNameLength];
+
+} NDS_CREDENTIAL, *PNDS_CREDENTIAL;
+
+typedef struct {
+
+ TAG_DATA_HEADER tdh;
+ WORD signDataLength;
+
+ //BYTE signData[signLength];
+
+} NDS_SIGNATURE, *PNDS_SIGNATURE;
+
+typedef struct {
+
+ TAG_DATA_HEADER tdh;
+ WORD keyDataLength;
+
+ //BYTE BsafeKeyData[keyDataLength];
+
+} NDS_PRIVATE_KEY, *PNDS_PRIVATE_KEY;
+
+typedef struct {
+
+ DWORD dwMaxFragSize;
+ DWORD dwRequestSize;
+ DWORD dwFragmentFlags;
+ DWORD dwNdsVerb;
+ DWORD dwReplyBufferSize;
+
+} NDS_REQUEST_HEADER, *PNDS_REQUEST_HEADER;
+
+typedef struct {
+
+ DWORD dwFragmentSize;
+ DWORD dwFraggerHandle;
+
+} NDS_REPLY_HEADER, *PNDS_REPLY_HEADER;
+
+#include <packoff.h>
+
+typedef struct _NDS_CONTEXT_HEAD {
+
+ //
+ // Node id and list entries.
+ //
+
+ NODE_TYPE_CODE ntc;
+ NODE_BYTE_SIZE nts;
+
+ //
+ // We can set this flag if we need to pause
+ // all tree activity (like, for a logout).
+ //
+
+ BOOLEAN CredentialLocked;
+
+ LIST_ENTRY Next;
+
+ //
+ // NDS tree name. Leave enough room for the munged credential name.
+ //
+
+ UNICODE_STRING NdsTreeName;
+ WCHAR NdsTreeNameBuffer[NDS_TREE_NAME_LEN + MAX_NDS_NAME_CHARS + 2];
+
+ //
+ // User's credentials.
+ //
+
+ PNDS_CREDENTIAL Credential;
+
+ //
+ // User's signature.
+ //
+
+ PNDS_SIGNATURE Signature;
+
+ //
+ // Password for this tree connection.
+ //
+
+ OEM_STRING Password;
+
+ //
+ // User's public key.
+ //
+
+ DWORD PublicKeyLen;
+ BYTE *PublicNdsKey;
+
+ //
+ // The current context for this tree.
+ //
+
+ UNICODE_STRING CurrentContext;
+ WCHAR CurrentContextString[MAX_NDS_NAME_CHARS];
+
+} NDS_SECURITY_CONTEXT, *PNDS_SECURITY_CONTEXT;
+
+typedef struct _NDS_CHPW_MSG {
+
+ DWORD challenge;
+ DWORD oldPwLength;
+ BYTE oldPwHash[16];
+ DWORD unknown;
+ DWORD newPwLength;
+ BYTE newPwHash[16];
+ ENC_BLOCK_HDR encPrivKeyHdr;
+
+ // BYTE encPrivKey[];
+
+} NDS_CHPW_MSG, *PNDS_CHPW_MSG;
+
+//
+// Credential list handling routines.
+//
+
+#define NwAcquireExclusiveCredList( pLogon) \
+ ExAcquireResourceExclusive( &((pLogon)->CredentialListResource), TRUE )
+
+#define NwAcquireSharedCredList( pLogon ) \
+ ExAcquireResourceShared( &((pLogon)->CredentialListResource), TRUE )
+
+#define NwReleaseCredList( pLogon ) \
+ ExReleaseResource( &((pLogon)->CredentialListResource) )
+
+#include <packon.h>
+
+typedef struct {
+
+ DWORD verb;
+ UINT count;
+ char *bufEnd;
+ PVOID nextItem;
+
+} NDS_TAG, *PNDS_TAG;
+
+#include <packoff.h>
+
+typedef struct _nds_list_response {
+
+ DWORD ccode;
+ DWORD iterationHandle;
+ DWORD numEntries;
+
+ //
+ // Followed by an array of these.
+ //
+ // struct {
+ // DWORD entryId;
+ // DWORD flags;
+ // DWORD subCount;
+ // DWORD modTime;
+ // NDS_STRING BaseClass;
+ // NDS_STRING entryName;
+ // } [];
+ //
+
+} NDS_LIST_RESPONSE, *PNDS_LIST_RESPONSE;
+
+typedef struct _locked_buffer {
+
+ //
+ // Describes a writeable response buffer
+ // that we have locked down for the transport.
+ //
+
+ PVOID pRecvBufferVa;
+ DWORD dwRecvLen;
+ PMDL pRecvMdl;
+ DWORD dwBytesWritten;
+
+} LOCKED_BUFFER, *PLOCKED_BUFFER;
+
+//
+// Some of the response packet formats from ndsapi32.h
+//
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD RemoteEntry;
+ DWORD EntryId;
+ DWORD ServerAddresses;
+ DWORD AddressType;
+ DWORD AddressLength;
+
+ //
+ // The address is of length
+ // AddressLength, of course.
+ //
+
+ BYTE Address[1];
+
+} NDS_WIRE_RESPONSE_RESOLVE_NAME, *PNDS_WIRE_RESPONSE_RESOLVE_NAME;
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD RemoteEntry;
+ DWORD EntryId;
+ DWORD Unknown;
+ DWORD ServerAddresses;
+ DWORD AddressType;
+ DWORD AddressLength;
+
+ //
+ // The address is of length
+ // AddressLength, of course.
+ //
+
+ BYTE Address[1];
+
+} NDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL,
+*PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL;
+
+//
+// Strings for searching ds attributes.
+//
+
+#define PUBLIC_KEY_ATTRIBUTE L"Public Key"
+#define VOLUME_ATTRIBUTE L"Volume"
+#define QUEUE_ATTRIBUTE L"Queue"
+#define DIR_MAP_ATTRIBUTE L"Directory Map"
+#define HOST_SERVER_ATTRIBUTE L"Host Server"
+#define HOST_VOLUME_ATTRIBUTE L"Host Resource Name"
+#define HOST_QUEUE_ATTRIBUTE L"CN"
+#define HOST_PATH_ATTRIBUTE L"Path"
+#define NT_GATEWAY_GROUP L"NTGATEWAY"
+#define GROUPS_ATTRIBUTE L"Group Membership"
+
+//
+// Prototypes from ndslogin.c
+//
+
+NTSTATUS
+NdsCanonUserName(
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN OUT PUNICODE_STRING puCanonUserName
+);
+
+NTSTATUS
+NdsCheckCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+);
+
+NTSTATUS
+NdsCheckCredentialsEx(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+);
+
+#define CREDENTIAL_READ 0
+#define CREDENTIAL_WRITE 1
+
+NTSTATUS
+NdsLookupCredentials(
+ IN PUNICODE_STRING puTreeName,
+ IN PLOGON pLogon,
+ OUT PNDS_SECURITY_CONTEXT *ppCredentials,
+ DWORD dwDesiredAccess,
+ BOOLEAN fCreate
+);
+
+NTSTATUS
+NdsGetCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+);
+
+NTSTATUS
+ChangeNdsPassword(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOID,
+ DWORD dwChallenge,
+ PBYTE pbOldPwHash,
+ PBYTE pbNewPwHash,
+ PNDS_PRIVATE_KEY pUserPrivKey,
+ PBYTE pServerPublicBsafeKey,
+ UINT ServerPubKeyLen
+);
+
+NTSTATUS
+DoNdsLogon(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+);
+
+NTSTATUS
+NdsTreeLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUser,
+ IN POEM_STRING pOemPassword,
+ IN POEM_STRING pOemNewPassword,
+ IN PLOGON pUserLogon
+);
+
+NTSTATUS
+BeginLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD userId,
+ OUT DWORD *loginId,
+ OUT DWORD *challenge
+);
+
+NTSTATUS
+FinishLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOID,
+ IN DWORD dwLoginFlags,
+ IN BYTE pbEncryptedChallenge[16],
+ IN BYTE *pbServerPublicBsafeKey,
+ IN int cbServerPublicBsafeKeyLen,
+ OUT BYTE *pbUserEncPrivateNdsKey,
+ OUT int *pcbUserEncPrivateNdsKeyLen,
+ OUT DWORD *pdwCredentialStartTime,
+ OUT DWORD *pdwCredentialEndTime
+);
+
+NTSTATUS
+NdsServerAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNDS_SECURITY_CONTEXT pNdsContext
+);
+
+NTSTATUS BeginAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserId,
+ OUT DWORD *pdwSvrRandom
+);
+
+NTSTATUS
+NdsLicenseConnection(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsUnlicenseConnection(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsLogoff(
+ IN PIRP_CONTEXT pIrpContext
+);
+
+//
+// Prototypes from fragex.c
+//
+
+NTSTATUS
+FragExWithWait(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD NdsVerb,
+ IN PLOCKED_BUFFER pReplyBuffer,
+ IN BYTE *NdsRequestStr,
+ ...
+);
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+);
+
+int
+_cdecl
+FormatBufS(
+ char *buf,
+ int bufLen,
+ const char *format,
+ ...
+);
+
+//
+// Prototypes from ndsfsctl.c
+//
+
+NTSTATUS
+NdsCreateTreeScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN OUT PSCB *ppScb,
+ IN PUNICODE_STRING puTree,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN DeferredLogon,
+ IN BOOLEAN DeleteOnClose
+);
+
+NTSTATUS
+NdsLookupServerName(
+ PSCB pTreeScb,
+ PIRP_CONTEXT pIrpContext,
+ IPXaddress *pDirServerAddress,
+ POEM_STRING pOemServerServerName
+);
+
+NTSTATUS
+DispatchNds(
+ IN ULONG IoctlCode,
+ IN PIRP_CONTEXT IrpContext
+);
+
+NTSTATUS
+PrepareLockedBufferFromFsd(
+ PIRP_CONTEXT pIrpContext,
+ PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+DoBrowseFsctl( PIRP_CONTEXT pIrpContext,
+ ULONG IoctlCode,
+ BOOL LockdownBuffer
+);
+
+NTSTATUS
+ConnectBinderyVolume(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+);
+
+NTSTATUS
+HandleVolumeAttach(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+);
+
+NTSTATUS
+NdsGetDsObjectFromPath(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING puDsObject
+);
+
+#define NDS_OBJECTTYPE_VOLUME 1
+#define NDS_OBJECTTYPE_QUEUE 2
+#define NDS_OBJECTTYPE_DIRMAP 3
+
+NTSTATUS
+NdsVerifyObject(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puVolumeObject,
+ IN BOOLEAN fAllowServerJump,
+ IN DWORD dwResolverFlags,
+ OUT PDWORD pdwVolumeOid,
+ OUT PDWORD pdwObjectType
+);
+
+NTSTATUS
+NdsMapObjectToServerShare(
+ PIRP_CONTEXT pIrpContext,
+ PSCB *ppScb,
+ PUNICODE_STRING puServerSharePath,
+ BOOLEAN CreateTreeConnection,
+ PDWORD pdwObjectId
+);
+
+NTSTATUS
+NdsVerifyContext(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTree,
+ PUNICODE_STRING puContext
+);
+
+NTSTATUS
+NdsRawFragex(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsChangePass(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsListTrees(
+ PIRP_CONTEXT pIrpContext
+);
+
+//
+// Browsing prototypes from ndsread.c
+//
+
+NTSTATUS
+NdsGetServerBasicName(
+ IN PUNICODE_STRING pServerX500Name,
+ IN OUT PUNICODE_STRING pServerName
+);
+
+NTSTATUS
+NdsCheckGroupMembership(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOid,
+ PUNICODE_STRING puGroupName
+);
+
+NTSTATUS
+NdsResolveName(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsGetObjectInfo(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsListSubordinates(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsReadAttributes(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsReadAttributesKm(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwObjectId,
+ PUNICODE_STRING puAttribute,
+ PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsOpenStream(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsSetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsGetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsVerifyTreeHandle(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsGetPrintQueueInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsGetVolumeInformation(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+//
+// Kernel mode browsing prototypes from ndsread.c
+//
+
+NTSTATUS
+NdsResolveNameKm (
+ PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puObjectName,
+ OUT DWORD *dwObjectId,
+ BOOLEAN AllowDsJump,
+ DWORD dwFlags
+);
+
+NTSTATUS
+NdsReadStringAttribute(
+ PIRP_CONTEXT pIrpContext,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puAttributeName,
+ OUT PUNICODE_STRING puAttributeVal
+);
+
+NTSTATUS
+NdsGetServerName(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING pUnicodeString
+);
+
+NTSTATUS
+NdsGetUserName(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOid,
+ OUT PUNICODE_STRING puUserName
+);
+
+//
+// Other helper prototypes from ndsread.c
+//
+
+VOID
+FreeNdsContext(
+ PNDS_SECURITY_CONTEXT pNdsContext
+);
+
+VOID
+NdsPing(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb
+);
+
+NTSTATUS
+NdsSelectConnection(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTreeName,
+ PUNICODE_STRING puUserName,
+ PUNICODE_STRING puPassword,
+ BOOL DeferredLogon,
+ BOOL UseBinderyConnections,
+ PNONPAGED_SCB *ppNpScb
+);
+
+NTSTATUS
+NdsCompletionCodetoNtStatus(
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsReadPublicKey(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD entryId,
+ OUT BYTE *pPubKeyVal,
+ IN DWORD *pPubKeyLen
+);
+
+int
+NdsGetBsafeKey(
+ UCHAR *pPubKey,
+ const int pubKeyLen,
+ UCHAR **ppBsafeKey
+);
+
+NTSTATUS
+NdsAllocateLockedBuffer(
+ PLOCKED_BUFFER NdsRequest,
+ DWORD BufferSize
+);
+
+NTSTATUS
+NdsFreeLockedBuffer(
+ PLOCKED_BUFFER NdsRequest
+);
+
+
diff --git a/private/nw/rdr/ndsread.c b/private/nw/rdr/ndsread.c
new file mode 100644
index 000000000..189fc9f1c
--- /dev/null
+++ b/private/nw/rdr/ndsread.c
@@ -0,0 +1,1190 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsRead.c
+
+Abstract:
+
+ This module implements the NDS read and request routines called
+ by the redirector natively and the support routines that go with
+ them.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+#pragma alloc_text( PAGE, NdsResolveNameKm )
+#pragma alloc_text( PAGE, NdsReadStringAttribute )
+#pragma alloc_text( PAGE, NdsReadAttributesKm )
+#pragma alloc_text( PAGE, NdsCompletionCodetoNtStatus )
+#pragma alloc_text( PAGE, FreeNdsContext )
+#pragma alloc_text( PAGE, NdsPing )
+#pragma alloc_text( PAGE, NdsGetUserName )
+#pragma alloc_text( PAGE, NdsGetServerBasicName )
+#pragma alloc_text( PAGE, NdsGetServerName )
+#pragma alloc_text( PAGE, NdsReadPublicKey )
+#pragma alloc_text( PAGE, NdsCheckGroupMembership )
+#pragma alloc_text( PAGE, NdsAllocateLockedBuffer )
+#pragma alloc_text( PAGE, NdsFreeLockedBuffer )
+
+NTSTATUS
+NdsResolveNameKm (
+ PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puObjectName,
+ OUT DWORD *dwObjectId,
+ BOOLEAN AllowDsJump,
+ DWORD dwFlags
+)
+/*++
+
+Description:
+
+ This is a wrapper routine to the browser routine NdsResolveName
+ for kernel components that need to resolve NDS names.
+
+Arguments:
+
+ pIrpContext - must point to the dir server that we should query
+ puObjectName - what we want to resolve
+ *dwObjectId - where to report the result
+ AllowDsJump - if we are referred to another dir server, can we jump?
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ PNDS_RESPONSE_RESOLVE_NAME Rsp;
+ LOCKED_BUFFER NdsRequestBuffer;
+
+ PSCB Scb, OldScb;
+ UNICODE_STRING ReferredServer;
+
+ PAGED_CODE();
+
+ //
+ // Prepare the request and response buffers.
+ //
+
+ Rrp = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequestBuffer, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( Rrp );
+ return Status;
+ }
+
+ //
+ // Set up the request packet.
+ //
+
+ RtlZeroMemory( Rrp, NDS_BUFFER_SIZE );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ResolveName.ObjectNameLength = puObjectName->Length;
+ Rrp->Parameters.ResolveName.ResolverFlags = dwFlags;
+
+ RtlCopyMemory( Rrp->Parameters.ResolveName.ObjectName,
+ puObjectName->Buffer,
+ puObjectName->Length );
+
+ //
+ // Do the resolve.
+ //
+
+ Status = NdsResolveName( pIrpContext, Rrp, &NdsRequestBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequestBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) NdsRequestBuffer.pRecvBufferVa;
+
+ if ( ( Rsp->RemoteEntry == RESOLVE_NAME_REFER_REMOTE ) &&
+ ( AllowDsJump ) ) {
+
+ //
+ // We need to queue this request to another server
+ // since this server doesn't have any details about
+ // the object.
+ //
+
+ ReferredServer.Length = (USHORT) Rsp->ServerNameLength;
+ ReferredServer.MaximumLength = ReferredServer.Length;
+ ReferredServer.Buffer = Rsp->ReferredServer;
+
+ OldScb = pIrpContext->pScb;
+ ASSERT( OldScb != NULL );
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &Scb,
+ pIrpContext,
+ &ReferredServer,
+ NULL,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Since we've jumped servers, dereference the old host
+ // server. The new one was referenced in CreateScb().
+ //
+
+ NwDereferenceScb( OldScb->pNpScb );
+
+ }
+
+ *dwObjectId = Rsp->EntryId;
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequestBuffer );
+ FREE_POOL( Rrp );
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadStringAttribute(
+ PIRP_CONTEXT pIrpContext,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puAttributeName,
+ OUT PUNICODE_STRING puAttributeVal
+)
+/*++
+
+Description:
+
+ This is a wrapper routine to the browser routine NdsReadAttributes
+ for kernel components that need to read NDS string attributes.
+
+Arguments:
+
+ pIrpContext - must point to the dir server that we should query
+ dwObjectId - oid of the object to query
+ puAttributeName - attribute that we want
+ puAttributeVal - value of the attribute
+
+--*/
+{
+
+ NTSTATUS Status;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD dwRequestSize, dwAttributeCount;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Set up the request and response buffers.
+ //
+
+ dwRequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puAttributeName->Length;
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) ALLOCATE_POOL( PagedPool, dwRequestSize );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( Rrp );
+ return Status;
+ }
+
+ //
+ // Prepare the request packet.
+ //
+
+ RtlZeroMemory( (BYTE *)Rrp, dwRequestSize );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ReadAttribute.ObjectId = dwObjectId;
+ Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE;
+ Rrp->Parameters.ReadAttribute.AttributeNameLength = puAttributeName->Length;
+
+ RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName,
+ puAttributeName->Buffer,
+ puAttributeName->Length );
+
+ //
+ // Make the request.
+ //
+
+ Status = NdsReadAttributes( pIrpContext, Rrp, &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the string attribute and return it.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G___D_S_T",
+ sizeof( DWORD ), // completion code
+ sizeof( DWORD ), // iter handle
+ sizeof( DWORD ), // info type
+ &dwAttributeCount, // attribute count
+ sizeof( DWORD ), // syntax id
+ NULL, // attribute name
+ sizeof( DWORD ), // number of values
+ puAttributeVal ); // attribute string
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+
+ExitWithCleanup:
+
+ FREE_POOL( Rrp );
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadAttributesKm(
+ PIRP_CONTEXT pIrpContext,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puAttributeName,
+ IN OUT PLOCKED_BUFFER pNdsRequest
+)
+/*++
+
+Description:
+
+ This is a wrapper routine to the browser routine NdsReadAttributes
+ for kernel components that need to read NDS string attributes and
+ get back the raw response.
+
+Arguments:
+
+ pIrpContext - must point to the dir server that we should query
+ dwObjectId - oid of the object to query
+ puAttributeName - attribute that we want
+ puAttributeVal - value of the attribute
+
+--*/
+{
+
+ NTSTATUS Status;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD dwRequestSize;
+
+ PAGED_CODE();
+
+ //
+ // Set up the request.
+ //
+
+ dwRequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puAttributeName->Length;
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) ALLOCATE_POOL( PagedPool, dwRequestSize );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory( (BYTE *)Rrp, dwRequestSize );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ReadAttribute.ObjectId = dwObjectId;
+ Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE;
+ Rrp->Parameters.ReadAttribute.AttributeNameLength = puAttributeName->Length;
+
+ RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName,
+ puAttributeName->Buffer,
+ puAttributeName->Length );
+
+ Status = NdsReadAttributes( pIrpContext, Rrp, pNdsRequest );
+
+ FREE_POOL( Rrp );
+ return Status;
+
+}
+
+//
+// Frosting and other helper wrapper functions.
+//
+
+NTSTATUS
+NdsCompletionCodetoNtStatus(
+ IN PLOCKED_BUFFER pLockedBuffer
+)
+/*+++
+
+Description:
+
+ Translates the completion code of an NDS transaction into
+ an NTSTATUS error code.
+
+Arguments:
+
+ pLockedBuffer - describes the locked reply buffer that contains
+ the response.
+
+---*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // Try to get the completion code from the user's buffer.
+ //
+
+ try {
+
+ Status = *((DWORD *)pLockedBuffer->pRecvBufferVa);
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+ //
+ // Decode it.
+ //
+
+ if ( Status != STATUS_SUCCESS ) {
+
+ DebugTrace( 0, Dbg, "NDS Error Code: %08lx\n", Status );
+
+ switch ( Status ) {
+
+ case -601: // No such entry.
+ case -602: // No such value.
+ case -603: // No such attribute.
+ case -607: // Illegal attribute.
+ case -610: // Illegal ds name.
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ break;
+
+ //
+ // These may only come on a VERIFY_PASSWORD verb, which
+ // we do not support. I'm not sure, though.
+ //
+
+ case -216: // Password too short.
+ case -215: // Duplicate password.
+
+ Status = STATUS_PASSWORD_RESTRICTION;
+ break;
+
+ case -222: // Expired password (and no grace logins left).
+
+ Status = STATUS_PASSWORD_EXPIRED;
+ break;
+
+ case -223: // Expired password; this is a successful grace login.
+
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ break;
+
+ case -639: // Incomplete authentication.
+ case -672: // No access.
+ case -677: // Invalid identity.
+ case -669: // Wrong password.
+
+ Status = STATUS_WRONG_PASSWORD;
+ break;
+
+ case -197: // Intruder lockout active.
+ case -220: // Account expired or disabled.
+
+ Status = STATUS_ACCOUNT_DISABLED;
+ break;
+
+ case -218: // Login time restrictions.
+
+ Status = STATUS_LOGIN_TIME_RESTRICTION;
+ break;
+
+ case -217: // Maximum logins exceeded.
+
+ Status = STATUS_CONNECTION_COUNT_LIMIT;
+ break;
+
+ default:
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ }
+
+ return Status;
+}
+
+VOID
+FreeNdsContext(
+ IN PNDS_SECURITY_CONTEXT pNdsSecContext
+)
+/*++
+
+Routine Description:
+
+ Free the referenced NDS context.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // Make sure this is a valid thing to be mucking with.
+ //
+
+ if ( !pNdsSecContext ||
+ pNdsSecContext->ntc != NW_NTC_NDS_CREDENTIAL ) {
+
+ DebugTrace( 0, Dbg, "FreeNdsContext didn't get an NDS context.\n", 0 );
+ return;
+ }
+
+ if ( pNdsSecContext->Credential ) {
+ FREE_POOL( pNdsSecContext->Credential );
+ }
+
+ if ( pNdsSecContext->Signature ) {
+ FREE_POOL( pNdsSecContext->Signature );
+ }
+
+ if ( pNdsSecContext->PublicNdsKey ) {
+ FREE_POOL( pNdsSecContext->PublicNdsKey );
+ }
+
+ if ( pNdsSecContext->Password.Buffer ) {
+ FREE_POOL( pNdsSecContext->Password.Buffer );
+ }
+
+ DebugTrace( 0, Dbg, "Freeing NDS security context at 0x%08lx\n", pNdsSecContext );
+
+ FREE_POOL( pNdsSecContext );
+
+ return;
+}
+
+VOID
+NdsPing(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb
+)
+/*++
+
+Routine Description:
+
+ Examine the server for NDS support and record the NDS tree
+ name in the SCB for later reference.
+
+Routine Arguments:
+
+ pIrpContext - A pointer to the IRP context for this transaction.
+ pScb - The SCB for the server.
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ OEM_STRING OemTreeName;
+ BYTE OemBuffer[NDS_TREE_NAME_LEN];
+
+ UNICODE_STRING TreeName;
+ WCHAR WBuffer[NDS_TREE_NAME_LEN];
+
+ UNICODE_STRING CredentialName;
+
+ PAGED_CODE();
+
+ pScb->NdsTreeName.Length = 0;
+
+ OemTreeName.Length = NDS_TREE_NAME_LEN;
+ OemTreeName.MaximumLength = NDS_TREE_NAME_LEN;
+ OemTreeName.Buffer = OemBuffer;
+
+ Status = ExchangeWithWait( pIrpContext,
+ SynchronousResponseCallback,
+ "N",
+ NDS_REQUEST, // NDS Function 104
+ NDS_PING ); // NDS Subfunction 1
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return;
+ }
+
+ //
+ // Pull out the padded NDS name
+ //
+
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N_r",
+ 2 * sizeof( DWORD ),
+ OemBuffer,
+ NDS_TREE_NAME_LEN );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return;
+ }
+
+ //
+ // Strip off the padding and convert to unicode.
+ //
+
+ while ( OemTreeName.Length > 0 &&
+ OemBuffer[OemTreeName.Length - 1] == '_' ) {
+ OemTreeName.Length--;
+ }
+
+ //
+ // Copy or munge the tree name, depending on the create type.
+ //
+
+ if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
+
+ TreeName.Length = 0;
+ TreeName.MaximumLength = sizeof( WBuffer );
+ TreeName.Buffer = WBuffer;
+
+ Status = RtlOemStringToUnicodeString( &TreeName,
+ &OemTreeName,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pScb->NdsTreeName.Length = 0;
+ return;
+ }
+
+ Status = BuildExCredentialServerName( &TreeName,
+ pIrpContext->Specific.Create.puCredentialName,
+ &CredentialName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return;
+ }
+
+ RtlCopyUnicodeString( &pScb->NdsTreeName, &CredentialName );
+
+ FREE_POOL( CredentialName.Buffer );
+
+ } else {
+
+ Status = RtlOemStringToUnicodeString( &pScb->NdsTreeName,
+ &OemTreeName,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pScb->NdsTreeName.Length = 0;
+ return;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "Nds Ping: Tree is ""%wZ""\n", &pScb->NdsTreeName);
+ return;
+
+}
+
+NTSTATUS
+NdsGetUserName(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOid,
+ OUT PUNICODE_STRING puUserName
+)
+/*++
+
+Description:
+
+ Get the fully distinguished name of the user referred to
+ by the provided oid.
+
+--*/
+{
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Allocate buffer space.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Make the request.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ &NdsRequest,
+ "DD",
+ 0,
+ dwUserOid );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_St",
+ sizeof( NDS_RESPONSE_GET_OBJECT_INFO ),
+ NULL,
+ puUserName );
+
+ //
+ // We either got it or we didn't.
+ //
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetServerBasicName(
+ IN PUNICODE_STRING pServerX500Name,
+ IN OUT PUNICODE_STRING pServerName
+) {
+
+ //
+ // Dig out the first component of the server's X.500 name.
+ // We count on the X500 prefix for the server object being "CN=",
+ // which might be unwise.
+ //
+
+ USHORT usPrefixSize, usSrv;
+
+ PAGED_CODE();
+
+ usPrefixSize = sizeof( "CN=" ) - sizeof( "" );
+ usSrv = 0;
+
+ if ( ( pServerX500Name->Buffer[0] != L'C' ) ||
+ ( pServerX500Name->Buffer[1] != L'N' ) ||
+ ( pServerX500Name->Buffer[2] != L'=' ) ) {
+
+ DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad prefix.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ( pServerX500Name->Length <= usPrefixSize ) {
+
+ DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad string length.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pServerName->Buffer = pServerX500Name->Buffer + usPrefixSize;
+ pServerName->Length = 0;
+
+ while ( ( usSrv < MAX_SERVER_NAME_LENGTH ) &&
+ ( pServerName->Buffer[usSrv++] != L'.' ) ) {
+
+ pServerName->Length += sizeof( WCHAR );
+ }
+
+ if ( usSrv == MAX_SERVER_NAME_LENGTH ) {
+
+ DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad server name response.\n", 0 );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ pServerName->MaximumLength = pServerName->Length;
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+NdsGetServerName(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING puServerName
+)
+/*++
+
+Description:
+
+ Get the fully distinguished name of the server that we
+ are connected to.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Make the request.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_GET_SERVER_ADDRESS,
+ &NdsRequest,
+ NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the server name from the response.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_T",
+ sizeof( DWORD ),
+ puServerName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadPublicKey(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwEntryId,
+ OUT BYTE *pPubKeyVal,
+ IN OUT DWORD *pPubKeyLen
+)
+/*++
+
+Routine Description:
+
+ Read the public key referenced by the given entry id.
+
+Routine Arguments:
+
+ pIrpContext - The IRP context for this connection.
+ dwEntryId - The entry id of the key.
+ pPubKeyVal - The destination buffer for the public key.
+ pPubKeyLen - The length of the public key destination buffer.
+
+Return Value:
+
+ The length of the key.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LOCKED_BUFFER NdsRequest;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ DWORD dwAttrNameLen, dwAttrLen, dwRcvLen, dwNumEntries;
+ BYTE *pRcv;
+
+ PAGED_CODE();
+
+ //
+ // Allocate and zero send and receive space.
+ //
+
+ Rrp = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( Rrp );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Fill in and prepare the request buffer.
+ //
+
+ RtlZeroMemory( Rrp, NDS_BUFFER_SIZE );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ReadAttribute.ObjectId = dwEntryId;
+ Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE;
+ Rrp->Parameters.ReadAttribute.AttributeNameLength =
+ sizeof( PUBLIC_KEY_ATTRIBUTE ) - sizeof( WCHAR );
+
+ RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName,
+ PUBLIC_KEY_ATTRIBUTE,
+ sizeof( PUBLIC_KEY_ATTRIBUTE ) - sizeof( WCHAR ) );
+
+ //
+ // Do the exchange.
+ //
+
+ Status = NdsReadAttributes( pIrpContext,
+ Rrp,
+ &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Skip over the attribute header and name.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_D",
+ 5 * sizeof( DWORD ),
+ &dwAttrNameLen );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Skip over the part we've parsed and pull out the attribute.
+ //
+
+ pRcv = (PBYTE)NdsRequest.pRecvBufferVa +
+ ( 6 * sizeof( DWORD ) ) +
+ ROUNDUP4(dwAttrNameLen);
+
+ dwRcvLen = NdsRequest.dwBytesWritten -
+ ( 6 * sizeof( DWORD ) ) +
+ ROUNDUP4(dwAttrNameLen);
+
+ Status = ParseResponse( NULL,
+ pRcv,
+ dwRcvLen,
+ "GDD",
+ &dwNumEntries,
+ &dwAttrLen );
+
+ if ( !NT_SUCCESS( Status ) ||
+ dwNumEntries != 1 ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "Public Key Length: %d\n", dwAttrLen );
+ pRcv += ( 2 * sizeof( DWORD ) );
+
+ if ( dwAttrLen <= *pPubKeyLen ) {
+
+ RtlCopyMemory( pPubKeyVal, pRcv, dwAttrLen );
+ *pPubKeyLen = dwAttrLen;
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Public key buffer is too small.\n", 0 );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ FREE_POOL( Rrp );
+ return Status;
+
+}
+
+NTSTATUS
+NdsCheckGroupMembership(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOid,
+ PUNICODE_STRING puGroupName
+) {
+
+ NTSTATUS Status;
+ UNICODE_STRING GroupListAttribute;
+ LOCKED_BUFFER NdsRequest;
+
+ PNDS_RESPONSE_READ_ATTRIBUTE pAttributeResponse;
+ PNDS_ATTRIBUTE pAttribute;
+ PBYTE pAttribData;
+ DWORD dwAttribLength, dwCurrentLength;
+ DWORD dwNumAttributes, dwCurrentAttribute;
+ UNICODE_STRING Group;
+ USHORT GroupLength;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &GroupListAttribute, GROUPS_ATTRIBUTE );
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsReadAttributesKm( pIrpContext,
+ dwUserOid,
+ &GroupListAttribute,
+ &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ pAttributeResponse = ( PNDS_RESPONSE_READ_ATTRIBUTE ) NdsRequest.pRecvBufferVa;
+ ASSERT( pAttributeResponse->NumAttributes > 0 );
+
+ //
+ // Skip over the response header and walk down the attribute
+ // until we get to the data. This is a little clunky.
+ //
+
+ pAttribute = ( PNDS_ATTRIBUTE ) ( pAttributeResponse + 1 );
+ dwCurrentLength = sizeof( NDS_RESPONSE_READ_ATTRIBUTE );
+
+ dwAttribLength = ROUNDUP4( pAttribute->AttribNameLength );
+ dwAttribLength += ( 2 * sizeof( DWORD ) );
+
+ //
+ // Make sure we don't walk past the end of the buffer because
+ // of a bad packet from the server.
+ //
+
+ if ( ( dwCurrentLength + dwAttribLength ) > NDS_BUFFER_SIZE ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pAttribData = ( ( BYTE * )pAttribute ) + dwAttribLength;
+ dwCurrentLength += dwAttribLength;
+
+ //
+ // This is DWORD aligned for four byte DWORDs.
+ //
+
+ if ( ( NDS_BUFFER_SIZE - dwCurrentLength ) < sizeof( DWORD ) ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ dwNumAttributes = * ( ( DWORD * ) pAttribData );
+
+ if ( dwNumAttributes == 0 ) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Each attribute is an NDS string DWORD aligned.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+
+ pAttribData += sizeof( DWORD );
+ dwCurrentLength += sizeof( DWORD );
+
+ for ( dwCurrentAttribute = 0;
+ dwCurrentAttribute < dwNumAttributes ;
+ dwCurrentAttribute++ ) {
+
+ Group.Length = Group.MaximumLength =
+ ( USHORT )( * ( ( DWORD * ) pAttribData ) ) - sizeof( WCHAR );
+ Group.Buffer = ( PWCHAR ) ( pAttribData + sizeof( DWORD ) );
+
+ if ( ( Group.Length + dwCurrentLength ) > NDS_BUFFER_SIZE ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Strip off the X500 prefix and the context.
+ //
+
+ GroupLength = 0;
+
+ while ( GroupLength < ( Group.Length / sizeof( WCHAR ) ) ) {
+
+ if ( Group.Buffer[GroupLength++] == L'=' ) {
+
+ Group.Buffer += 1;
+ Group.Length -= sizeof( WCHAR );
+ Group.MaximumLength -= sizeof( WCHAR );
+
+ GroupLength = ( Group.Length / sizeof( WCHAR ) );
+ }
+
+ Group.Buffer += 1;
+ Group.Length -= sizeof( WCHAR );
+ Group.MaximumLength -= sizeof( WCHAR );
+ }
+
+ GroupLength = 0;
+
+ while ( GroupLength < ( Group.Length / sizeof( WCHAR ) ) ) {
+
+ if ( Group.Buffer[GroupLength++] == L'.' ) {
+ Group.Length = ( GroupLength - 1 ) * sizeof( WCHAR );
+ Group.MaximumLength = Group.Length;
+ break;
+ }
+ }
+
+ if ( RtlEqualUnicodeString( puGroupName, &Group, TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Group check for %wZ succeeded.\n", &Group );
+ Status = STATUS_SUCCESS;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the attribute size and process the next entry.
+ //
+
+ dwAttribLength = ROUNDUP4( * ( ( DWORD * ) pAttribData ) );
+ dwAttribLength += sizeof( DWORD );
+ pAttribData += dwAttribLength;
+ dwCurrentLength += dwAttribLength;
+
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+}
+
+
+NTSTATUS
+NdsAllocateLockedBuffer(
+ PLOCKED_BUFFER NdsRequest,
+ DWORD BufferSize
+)
+/*++
+
+Description:
+
+ Allocate a buffer for io. Lock it down and fill in the
+ buffer data structure that we pass around.
+
+--*/
+{
+
+ PAGED_CODE();
+
+ NdsRequest->pRecvBufferVa = ALLOCATE_POOL( PagedPool, BufferSize );
+
+ if ( !NdsRequest->pRecvBufferVa ) {
+ DebugTrace( 0, Dbg, "Couldn't allocate locked io buffer.\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NdsRequest->dwRecvLen = BufferSize;
+ NdsRequest->pRecvMdl = ALLOCATE_MDL( NdsRequest->pRecvBufferVa,
+ BufferSize,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !NdsRequest->pRecvMdl ) {
+ DebugTrace( 0, Dbg, "Couldn't allocate mdl for locked io buffer.\n", 0 );
+ FREE_POOL( NdsRequest->pRecvBufferVa );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ MmProbeAndLockPages( NdsRequest->pRecvMdl,
+ KernelMode,
+ IoWriteAccess );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+NdsFreeLockedBuffer(
+ PLOCKED_BUFFER NdsRequest
+)
+/*++
+
+Description:
+
+ Free a buffer allocated for io.
+
+--*/
+{
+
+ PAGED_CODE();
+
+ MmUnlockPages( NdsRequest->pRecvMdl );
+ FREE_MDL( NdsRequest->pRecvMdl );
+ FREE_POOL( NdsRequest->pRecvBufferVa );
+ return STATUS_SUCCESS;
+
+}
diff --git a/private/nw/rdr/nodetype.h b/private/nw/rdr/nodetype.h
new file mode 100644
index 000000000..7abbe1537
--- /dev/null
+++ b/private/nw/rdr/nodetype.h
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ NodeType.h
+
+Abstract:
+
+ This module defines all of the node type codes used in this development
+ shell. Every major data structure in the file system is assigned a node
+ type code that is. This code is the first CSHORT in the structure and is
+ followed by a CSHORT containing the size, in bytes, of the structure.
+
+Author:
+
+ Colin Watson [ColinW] 18-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NODETYPE_
+#define _NODETYPE_
+
+typedef CSHORT NODE_TYPE_CODE;
+typedef NODE_TYPE_CODE *PNODE_TYPE_CODE;
+
+#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000)
+
+#define NW_NTC_SCB ((NODE_TYPE_CODE)0x0F01)
+#define NW_NTC_SCBNP ((NODE_TYPE_CODE)0x0F02)
+#define NW_NTC_FCB ((NODE_TYPE_CODE)0x0F03)
+#define NW_NTC_DCB ((NODE_TYPE_CODE)0x0F04)
+#define NW_NTC_VCB ((NODE_TYPE_CODE)0x0F05)
+#define NW_NTC_ICB ((NODE_TYPE_CODE)0x0F06)
+#define NW_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0F07)
+#define NW_NTC_NONPAGED_FCB ((NODE_TYPE_CODE)0x0F08)
+#define NW_NTC_RCB ((NODE_TYPE_CODE)0x0F0A)
+#define NW_NTC_ICB_SCB ((NODE_TYPE_CODE)0x0F0B)
+#define NW_NTC_PID ((NODE_TYPE_CODE)0x0F0C)
+#define NW_NTC_FILE_LOCK ((NODE_TYPE_CODE)0x0F0D)
+#define NW_NTC_LOGON ((NODE_TYPE_CODE)0x0F0E)
+#define NW_NTC_MINI_IRP_CONTEXT ((NODE_TYPE_CODE)0x0F0F)
+#define NW_NTC_NDS_CREDENTIAL ((NODE_TYPE_CODE)0x0F10)
+
+typedef CSHORT NODE_BYTE_SIZE;
+
+//
+// So all records start with
+//
+// typedef struct _RECORD_NAME {
+// NODE_TYPE_CODE NodeTypeCode;
+// NODE_BYTE_SIZE NodeByteSize;
+// :
+// } RECORD_NAME;
+// typedef RECORD_NAME *PRECORD_NAME;
+//
+
+#define NodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr)))
+
+#endif // _NODETYPE_
diff --git a/private/nw/rdr/nwrdr.rc b/private/nw/rdr/nwrdr.rc
new file mode 100644
index 000000000..dff665f61
--- /dev/null
+++ b/private/nw/rdr/nwrdr.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_NETWORK
+#define VER_FILEDESCRIPTION_STR "NetWare Redirector File System Driver"
+#define VER_INTERNALNAME_STR "nwrdr.sys"
+
+#include "common.ver"
+
diff --git a/private/nw/rdr/pid.c b/private/nw/rdr/pid.c
new file mode 100644
index 000000000..375fba553
--- /dev/null
+++ b/private/nw/rdr/pid.c
@@ -0,0 +1,454 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Pid.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to map 32 bit NT pid values to unique 8 bit
+ NetWare values.
+
+ The technique used is to maintain a table of up to 256 entries.
+ The index of each entry corresponds directly to the 8 bit pid
+ values. Each table entry contains the 32 bit pid of the process
+ that has obtained exclusive access to the pid and the number of
+ handles opened by that process to this server.
+
+ This architecture limits the number of processes on the NT machine
+ communicating with any one server to 256.
+
+ Note: This package assumes that the size that the PidTable grows is
+ a factor of 256-<initial entries>. This ensures that running out of
+ valid entries in the table will occur when 256 entries have been
+ allocated.
+
+Author:
+
+ Colin Watson [ColinW] 02-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+#define INITIAL_MAPPID_ENTRIES 8
+#define MAPPID_INCREASE 8
+#define MAX_PIDS 256
+
+#define PID_FLAG_EOJ_REQUIRED 0x00000001 // EOJ required for this PID
+
+typedef struct _NW_PID_TABLE_ENTRY {
+ ULONG Pid32;
+ ULONG ReferenceCount;
+ ULONG Flags;
+} NW_PID_TABLE_ENTRY, *PNW_PID_TABLE_ENTRY;
+
+typedef struct _NW_PID_TABLE {
+
+ //
+ // Type and size of this record (must be NW_NTC_PID)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ int ValidEntries;
+ NW_PID_TABLE_ENTRY PidTable[0];
+} NW_PID_TABLE, *PNW_PID_TABLE;
+
+
+
+PNW_PID_TABLE PidTable;
+ERESOURCE PidResource;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwInitializePidTable )
+#pragma alloc_text( PAGE, NwUninitializePidTable )
+#pragma alloc_text( PAGE, NwMapPid )
+#pragma alloc_text( PAGE, NwSetEndOfJobRequired )
+#pragma alloc_text( PAGE, NwUnmapPid )
+#endif
+
+
+BOOLEAN
+NwInitializePidTable(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Creates a table for the MapPid package. The initial table has room for
+ INITIAL_MAPPID_ENTRIES entries.
+
+Arguments:
+
+
+Return Value:
+
+ NTSTATUS of result.
+
+--*/
+
+{
+ int i;
+ PNW_PID_TABLE TempPid =
+ ALLOCATE_POOL( PagedPool,
+ FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * INITIAL_MAPPID_ENTRIES ));
+
+ PAGED_CODE();
+
+ if (TempPid == NULL) {
+ return( FALSE );
+ }
+
+ TempPid->NodeByteSize = FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * INITIAL_MAPPID_ENTRIES );
+
+ TempPid->NodeTypeCode = NW_NTC_PID;
+
+ TempPid->ValidEntries = INITIAL_MAPPID_ENTRIES;
+
+ //
+ // Set the ref count for all PIDs to 0, except for pid 0. We
+ // do this so that we don't allocate PID 0.
+ //
+
+ TempPid->PidTable[0].ReferenceCount = 1;
+ for (i = 1; i < INITIAL_MAPPID_ENTRIES ; i++ ) {
+ TempPid->PidTable[i].ReferenceCount = 0;
+ }
+
+ PidTable = TempPid;
+ ExInitializeResource( &PidResource );
+}
+
+VOID
+NwUninitializePidTable(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Deletes a table created by the MapPid package.
+
+Arguments:
+
+ Pid - Supplies the table to be deleted.
+
+Return Value:
+
+--*/
+
+{
+#ifdef NWDBG
+ int i;
+#endif
+
+ PAGED_CODE();
+
+#ifdef NWDBG
+ ASSERT(PidTable->NodeTypeCode == NW_NTC_PID);
+ ASSERT(PidTable->PidTable[0].ReferenceCount == 1);
+
+ for (i = 1; i < PidTable->ValidEntries; i++ ) {
+ ASSERT(PidTable->PidTable[i].ReferenceCount == 0);
+ }
+#endif
+
+ FREE_POOL( PidTable );
+
+ ExDeleteResource( &PidResource );
+ return;
+
+}
+
+NTSTATUS
+NwMapPid(
+ IN ULONG Pid32,
+ OUT PUCHAR Pid8
+ )
+/*++
+
+Routine Description:
+
+ Obtain an 8 bit unique pid for this process. Either use a previosly
+ assigned pid for this process or assign an unused value.
+
+Arguments:
+
+ Pid - Supplies the datastructure used by MapPid to assign pids for
+ this server.
+
+ Pid32 - Supplies the NT pid to be mapped.
+
+ Pid8 - Returns the 8 bit Pid.
+
+Return Value:
+
+ NTSTATUS of result.
+
+--*/
+{
+ int i;
+ int FirstFree = -1;
+ int NewEntries;
+ PNW_PID_TABLE TempPid;
+
+ PAGED_CODE();
+
+ ExAcquireResourceExclusive( &PidResource, TRUE );
+
+ // DebugTrace(0, Dbg, "NwMapPid for %08lx\n", Pid32);
+
+ for (i=0; i < (PidTable)->ValidEntries ; i++ ) {
+
+ if ((PidTable)->PidTable[i].Pid32 == Pid32) {
+
+ //
+ // This process already has an 8 bit pid value assigned.
+ // Increment the reference and return.
+ //
+
+ (PidTable)->PidTable[i].ReferenceCount++;
+ *Pid8 = i;
+
+ // DebugTrace(0, Dbg, "NwMapPid found %08lx\n", (DWORD)i);
+
+ ExReleaseResource( &PidResource );
+ ASSERT( *Pid8 != 0 );
+ return( STATUS_SUCCESS );
+ }
+
+ if ((FirstFree == -1) &&
+ ((PidTable)->PidTable[i].ReferenceCount == 0)) {
+
+ //
+ // i is the lowest free 8 bit Pid.
+ //
+
+ FirstFree = i;
+ }
+ }
+
+ //
+ // This process does not have a pid assigned.
+ //
+
+ if ( FirstFree != -1 ) {
+
+ //
+ // We had an empty slot so assign it to this process.
+ //
+
+ (PidTable)->PidTable[FirstFree].ReferenceCount++;
+ (PidTable)->PidTable[FirstFree].Pid32 = Pid32;
+ *Pid8 = FirstFree;
+
+ DebugTrace(0, DEBUG_TRACE_ICBS, "NwMapPid maps %08lx\n", (DWORD)FirstFree);
+
+ ExReleaseResource( &PidResource );
+ ASSERT( *Pid8 != 0 );
+ return( STATUS_SUCCESS );
+ }
+
+ if ( (PidTable)->ValidEntries == MAX_PIDS ) {
+
+ //
+ // We've run out of 8 bit pids.
+ //
+
+ ExReleaseResource( &PidResource );
+
+#ifdef NWDBG
+ //
+ // temporary code to find the PID leak. BUGBUG
+ //
+ DumpIcbs() ;
+ ASSERT(FALSE) ;
+#endif
+
+ return(STATUS_TOO_MANY_OPENED_FILES);
+ }
+
+ //
+ // Grow the table by MAPPID_INCREASE entries.
+ //
+
+ NewEntries = (PidTable)->ValidEntries + MAPPID_INCREASE;
+
+ TempPid =
+ ALLOCATE_POOL( PagedPool,
+ FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * NewEntries ));
+
+ if (TempPid == NULL) {
+ ExReleaseResource( &PidResource );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ RtlMoveMemory(
+ TempPid,
+ (PidTable),
+ FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * (PidTable)->ValidEntries ));
+
+ TempPid->NodeByteSize = FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * NewEntries );
+
+ for ( i = (PidTable)->ValidEntries; i < NewEntries ; i++ ) {
+ TempPid->PidTable[i].ReferenceCount = 0;
+ }
+
+ TempPid->ValidEntries = NewEntries;
+
+ //
+ // Save the index of the first free entry.
+ //
+
+ i = (PidTable)->ValidEntries;
+
+ //
+ // The new table is initialized. Free up the old table and return
+ // the first of the new entries.
+ //
+
+ FREE_POOL(PidTable);
+ PidTable = TempPid;
+
+ (PidTable)->PidTable[i].ReferenceCount = 1;
+ (PidTable)->PidTable[i].Pid32 = Pid32;
+ *Pid8 = i;
+
+ DebugTrace(0, DEBUG_TRACE_ICBS, "NwMapPid grows & maps %08lx\n", (DWORD)i);
+
+ ExReleaseResource( &PidResource );
+ return( STATUS_SUCCESS );
+}
+
+VOID
+NwSetEndOfJobRequired(
+ IN UCHAR Pid8
+ )
+/*++
+
+Routine Description:
+
+ Mark a PID as must send End Of Job when the pid reference count
+ reaches zero.
+
+Arguments:
+
+ Pid8 - The 8 bit Pid to mark.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT( Pid8 != 0 );
+
+ // DebugTrace(0, Dbg, "NwSetEndofJob for %08lx\n", (DWORD)Pid8);
+ SetFlag( PidTable->PidTable[Pid8].Flags, PID_FLAG_EOJ_REQUIRED );
+ return;
+}
+
+
+VOID
+NwUnmapPid(
+ IN UCHAR Pid8,
+ IN PIRP_CONTEXT IrpContext OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine dereference an 8 bit PID. If the reference count reaches
+ zero and this PID is marked End Of Job required, this routine will
+ also send an EOJ NCP for this PID.
+
+Arguments:
+
+ Pid8 - The 8 bit Pid to mark.
+
+ IrpContext - The IrpContext for the IRP in progress.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BOOLEAN EndOfJob;
+
+ PAGED_CODE();
+
+ ASSERT( Pid8 != 0 );
+
+ // DebugTrace(0, Dbg, "NwUnmapPid %08lx\n", (DWORD)Pid8);
+ if ( BooleanFlagOn( PidTable->PidTable[Pid8].Flags, PID_FLAG_EOJ_REQUIRED ) &&
+ IrpContext != NULL ) {
+
+ //
+ // The End of job flag is set. Obtain a position at the front of
+ // the SCB queue, so that if we need to set an EOJ NCP, we needn't
+ // wait for the SCB queue while holding the PID table lock.
+ //
+
+ EndOfJob = TRUE;
+ NwAppendToQueueAndWait( IrpContext );
+ } else {
+ EndOfJob = FALSE;
+ }
+
+ //
+ // The PidResource lock controls the reference counts.
+ //
+
+ ExAcquireResourceExclusive( &PidResource, TRUE );
+
+ if ( --(PidTable)->PidTable[Pid8].ReferenceCount == 0 ) {
+
+ //
+ // Done with this PID, send an EOJ if necessary.
+ //
+
+ // DebugTrace(0, Dbg, "NwUnmapPid (ref=0) %08lx\n", (DWORD)Pid8);
+ (PidTable)->PidTable[Pid8].Flags = 0;
+ (PidTable)->PidTable[Pid8].Pid32 = 0;
+
+ if ( EndOfJob ) {
+ (VOID) ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-",
+ NCP_END_OF_JOB );
+ }
+ }
+
+ if ( EndOfJob ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ ASSERT((PidTable)->PidTable[Pid8].ReferenceCount>=0);
+
+ ExReleaseResource( &PidResource );
+}
+
diff --git a/private/nw/rdr/procs.h b/private/nw/rdr/procs.h
new file mode 100644
index 000000000..254db4ddd
--- /dev/null
+++ b/private/nw/rdr/procs.h
@@ -0,0 +1,1830 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Procs.h
+
+Abstract:
+
+ This module defines all of the globally used procedures in the NetWare
+ redirector.
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NWPROCS_
+#define _NWPROCS_
+
+#ifndef QFE_BUILD
+#define IFS 1
+#define NWFASTIO 1
+#endif
+
+#ifdef IFS
+
+ #include <ntifs.h>
+
+#else
+
+ #include <ntos.h>
+ #include <ntioapi.h>
+ #include <zwapi.h>
+ #include <FsRtl.h>
+
+#endif
+
+#include <string.h>
+#include <Tdi.h>
+#include <TdiKrnl.h>
+#include <Status.h>
+#include <nwstatus.h>
+
+// Netware and Netware redirector specific includes
+
+#ifndef DBG
+#define DBG 0
+#endif
+
+#if !DBG
+#undef NWDBG
+#endif
+
+#if NWDBG
+#define PAGED_DBG 1
+#endif
+#ifdef PAGED_DBG
+#undef PAGED_CODE
+#define PAGED_CODE() \
+ struct { ULONG bogus; } ThisCodeCantBePaged; \
+ ThisCodeCantBePaged; \
+ if (KeGetCurrentIrql() > APC_LEVEL) { \
+ KdPrint(( "EX: Pageable code called at IRQL %d\n", KeGetCurrentIrql() )); \
+ ASSERT(FALSE); \
+ }
+#define PAGED_CODE_CHECK() if (ThisCodeCantBePaged) ;
+extern ULONG ThisCodeCantBePaged;
+#else
+#define PAGED_CODE_CHECK()
+#endif
+
+#include <NtDDNwfs.h>
+#include "Const.h"
+#include "Nodetype.h"
+#include "ncp.h"
+#include "Struct.h"
+#include "Data.h"
+#include "Exchange.h"
+#include <NwEvent.h>
+
+//
+// NDS Additions.
+//
+
+#include <nds.h>
+#include "ndsprocs.h"
+
+// Attach.c
+
+NTSTATUS
+ConnectToServer(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PSCB *pScbCollision
+);
+
+NTSTATUS
+ProcessFindNearest(
+ IN struct _IRP_CONTEXT* pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+NTSTATUS
+CrackPath (
+ IN PUNICODE_STRING BaseName,
+ OUT PUNICODE_STRING DriveName,
+ OUT PWCHAR DriveLetter,
+ OUT PUNICODE_STRING ServerName,
+ OUT PUNICODE_STRING VolumeName,
+ OUT PUNICODE_STRING PathName,
+ OUT PUNICODE_STRING FileName,
+ OUT PUNICODE_STRING FullName OPTIONAL
+ );
+
+NTSTATUS
+CheckScbSecurity(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN fDeferLogon
+);
+
+NTSTATUS
+ConnectScb(
+ IN PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection,
+ IN BOOLEAN ExistingScb
+);
+
+#define IS_ANONYMOUS_SCB( pScb ) \
+ ( (pScb->UidServerName).Length == 0 )
+
+NTSTATUS
+CreateScb(
+ OUT PSCB *Scb,
+ IN PIRP_CONTEXT pIrpC,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection
+ );
+
+VOID
+DestroyAllScb(
+ VOID
+ );
+
+VOID
+InitializeAttach (
+ VOID
+ );
+
+NTSTATUS
+OpenScbSockets(
+ PIRP_CONTEXT pIrpC,
+ PNONPAGED_SCB pNpScb
+ );
+
+PNONPAGED_SCB
+SelectConnection(
+ PNONPAGED_SCB NpScb
+ );
+
+VOID
+NwLogoffAndDisconnect(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+VOID
+NwLogoffAllServers(
+ PIRP_CONTEXT pIrpContext,
+ PLARGE_INTEGER Uid
+ );
+
+VOID
+NwDeleteScb(
+ PSCB pScb
+ );
+
+NTSTATUS
+NegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ BOOLEAN *LIPNegotiated
+ );
+
+VOID
+RenegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+BOOLEAN
+NwFindScb(
+ OUT PSCB *ppScb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UidServerName,
+ IN PUNICODE_STRING ServerName
+ );
+
+NTSTATUS
+QueryServersAddress(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNearestScb,
+ PUNICODE_STRING pServerName,
+ IPXaddress *pServerAddress
+ );
+
+VOID
+TreeConnectScb(
+ IN PSCB Scb
+ );
+
+NTSTATUS
+TreeDisconnectScb(
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ );
+
+VOID
+ReconnectScb(
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB pScb
+ );
+
+// Cache.c
+
+ULONG
+CacheRead(
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToRead,
+ IN PVOID UserBuffer
+#if NWFASTIO
+ , IN BOOLEAN WholeBufferOnly
+#endif
+ );
+
+BOOLEAN
+CacheWrite(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToWrite,
+ IN PVOID UserBuffer
+ );
+
+ULONG
+CalculateReadAheadSize(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG CacheReadSize,
+ IN ULONG FileOffset,
+ IN ULONG ByteCount
+ );
+
+NTSTATUS
+FlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ );
+
+NTSTATUS
+AcquireFcbAndFlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ );
+
+// Callback.c
+
+
+NTSTATUS
+SynchronousResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+NTSTATUS
+AsynchResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+NTSTATUS
+NcpSearchFileCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PUCHAR RspData
+ );
+
+// Cleanup.c
+
+NTSTATUS
+NwFsdCleanup (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// Close.c
+
+NTSTATUS
+NwFsdClose (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// Create.c
+
+NTSTATUS
+NwFsdCreate (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ReadAttachEas(
+ IN PIRP Irp,
+ OUT PUNICODE_STRING UserName,
+ OUT PUNICODE_STRING Password,
+ OUT PULONG ShareType,
+ OUT PDWORD CredentialExtension
+ );
+
+// Convert.c
+
+NTSTATUS
+pNwErrorToNtStatus(
+ UCHAR Error
+ );
+
+NTSTATUS
+NwBurstResultToNtStatus(
+ ULONG Result
+ );
+
+#define NwErrorToNtStatus( STATUS ) \
+ (STATUS == 0 )? STATUS_SUCCESS : pNwErrorToNtStatus(STATUS)
+
+NTSTATUS
+NwConnectionStatusToNtStatus(
+ UCHAR NwStatus
+ );
+
+UCHAR
+NtAttributesToNwAttributes(
+ ULONG FileAttributes
+ );
+
+UCHAR
+NtToNwShareFlags(
+ ULONG DesiredAccess,
+ ULONG NtShareFlags
+ );
+
+LARGE_INTEGER
+NwDateTimeToNtTime(
+ USHORT Date,
+ USHORT Time
+ );
+
+NTSTATUS
+NwNtTimeToNwDateTime (
+ IN LARGE_INTEGER NtTime,
+ IN PUSHORT NwDate,
+ IN PUSHORT NwTime
+ );
+
+// Data.c
+
+VOID
+NwInitializeData(
+ VOID
+ );
+
+// Debug.c
+
+#ifdef NWDBG
+
+ULONG
+NwMemDbg (
+ IN PCH Format,
+ ...
+ );
+
+VOID
+RealDebugTrace(
+ IN LONG Indent,
+ IN ULONG Level,
+ IN PCH Message,
+ IN PVOID Parameter
+ );
+
+VOID
+dump(
+ IN ULONG Level,
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+dumpMdl(
+ IN ULONG Level,
+ IN PMDL Mdl
+ );
+
+VOID
+DumpIcbs(
+ VOID
+ ) ;
+
+
+PVOID
+NwAllocatePool(
+ ULONG Type,
+ ULONG Size,
+ BOOLEAN RaiseStatus
+ );
+
+VOID
+NwFreePool(
+ PVOID Buffer
+ );
+
+PIRP
+NwAllocateIrp(
+ CCHAR Size,
+ BOOLEAN ChargeQuota
+ );
+
+VOID
+NwFreeIrp(
+ PIRP Irp
+ );
+
+PMDL
+NwAllocateMdl(
+ PVOID Va,
+ ULONG Length,
+ BOOLEAN Secondary,
+ BOOLEAN ChargeQuota,
+ PIRP Irp,
+ PUCHAR FileName,
+ int Line
+ );
+
+VOID
+NwFreeMdl(
+ PMDL Mdl
+ );
+
+#else
+#define dump( level, pointer, length ) { NOTHING;}
+#endif
+
+
+// Deviosup.c
+
+VOID
+NwMapUserBuffer (
+ IN OUT PIRP Irp,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PVOID *UserBuffer
+ );
+
+VOID
+NwLockUserBuffer (
+ IN OUT PIRP Irp,
+ IN LOCK_OPERATION Operation,
+ IN ULONG BufferLength
+ );
+
+// Dir.c
+
+NTSTATUS
+NwFsdDirectoryControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// Encrypt.c
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+// Exchange.c
+
+BOOLEAN
+AppendToScbQueue(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_SCB NpScb
+ );
+
+VOID
+PreparePacket(
+ PIRP_CONTEXT pIrpContext,
+ PIRP pOriginalIrp,
+ PMDL pMdl
+ );
+
+NTSTATUS
+PrepareAndSendPacket(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+SendPacket(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+VOID
+SendNow(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+VOID
+SetEvent(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+_cdecl
+ExchangeWithWait(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ );
+
+NTSTATUS
+_cdecl
+BuildRequestPacket(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PIRP_CONTEXT IrpContext,
+ PUCHAR RequestHeader,
+ ULONG RequestLength,
+ char* f,
+ ... // format specific parameters
+ );
+
+NTSTATUS
+ParseNcpResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_RESPONSE Response
+ );
+
+BOOLEAN
+VerifyResponse(
+ PIRP_CONTEXT pIrpContext,
+ PVOID Response
+ );
+
+VOID
+FreeReceiveIrp(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NewRouteRetry(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NewRouteBurstRetry(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+ULONG
+MdlLength (
+ register IN PMDL Mdl
+ );
+
+VOID
+NwProcessSendBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ );
+
+VOID
+NwProcessSendBurstSuccess(
+ PNONPAGED_SCB NpScb
+ );
+
+VOID
+NwProcessReceiveBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ );
+
+VOID
+NwProcessReceiveBurstSuccess(
+ PNONPAGED_SCB NpScb
+ );
+
+VOID
+NwProcessPositiveAck(
+ PNONPAGED_SCB NpScb
+ );
+
+// Errorlog.c
+
+VOID
+_cdecl
+Error(
+ IN ULONG UniqueErrorCode,
+ IN NTSTATUS NtStatusCode,
+ IN PVOID ExtraInformationBuffer,
+ IN USHORT ExtraInformationLength,
+ IN USHORT NumberOfInsertionStrings,
+ ...
+ );
+
+// FileInfo.c
+
+NTSTATUS
+NwFsdQueryInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwFsdSetInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwDeleteFile(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+ULONG
+OccurenceCount (
+ IN PUNICODE_STRING String,
+ IN WCHAR SearchChar
+ );
+
+#if NWFASTIO
+BOOLEAN
+NwFastQueryBasicInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_BASIC_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+BOOLEAN
+NwFastQueryStandardInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_STANDARD_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+#endif
+
+// Filobsup.c
+
+VOID
+NwSetFileObject (
+ IN PFILE_OBJECT FileObject OPTIONAL,
+ IN PVOID FsContext,
+ IN PVOID FsContext2
+ );
+
+NODE_TYPE_CODE
+NwDecodeFileObject (
+ IN PFILE_OBJECT FileObject,
+ OUT PVOID *FsContext,
+ OUT PVOID *FsContext2
+ );
+
+BOOLEAN
+NwIsIrpTopLevel (
+ IN PIRP Irp
+ );
+
+// Fsctl.c
+
+NTSTATUS
+NwFsdFileSystemControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwCommonFileSystemControl (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwFsdDeviceIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+#ifdef _PNP_POWER
+
+VOID
+HandleTdiBindMessage(
+ IN PUNICODE_STRING DeviceName
+);
+
+VOID
+HandleTdiUnbindMessage(
+ IN PUNICODE_STRING DeviceName
+);
+
+#endif
+
+PLOGON
+FindUser(
+ IN PLARGE_INTEGER Uid,
+ IN BOOLEAN ExactMatch
+ );
+
+LARGE_INTEGER
+GetUid(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
+ );
+
+PLOGON
+FindUserByName(
+ IN PUNICODE_STRING UserName
+);
+
+VOID
+LazySetShareable(
+ PIRP_CONTEXT IrpContext,
+ PICB pIcb,
+ PFCB pFcb
+);
+
+// FspDisp.c
+
+VOID
+NwFspDispatch (
+ IN PVOID Context
+ );
+
+NTSTATUS
+NwPostToFsp (
+ IN PIRP_CONTEXT IrpContext,
+ IN BOOLEAN MarkIrpPending
+ );
+
+// hack.c
+
+NTSTATUS
+_cdecl
+BuildNcpResponse(
+ PIRP_CONTEXT pIrpC,
+ char* f,
+ char Error,
+ char Status,
+ ...
+ );
+
+NTSTATUS
+HackSendMessage(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+_cdecl
+HackParseResponse(
+ PUCHAR Response,
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+// Ipx.c
+
+NTSTATUS
+IpxOpenHandle(
+ OUT PHANDLE pHandle,
+ OUT PDEVICE_OBJECT* ppDeviceObject,
+ OUT PFILE_OBJECT* pFileObject,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength
+ );
+
+NTSTATUS
+IpxOpen(
+ VOID
+ );
+
+VOID
+IpxClose(
+ VOID
+ );
+
+VOID
+BuildIpxAddress(
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PTA_IPX_ADDRESS NetworkName
+ );
+
+VOID
+BuildIpxAddressEa (
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PVOID NetworkName
+ );
+
+NTSTATUS
+SetEventHandler (
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG EventType,
+ IN PVOID pEventHandler,
+ IN PVOID pContext
+ );
+
+NTSTATUS
+GetMaximumPacketSize(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PULONG pMaximumPacketSize
+ );
+
+NTSTATUS
+GetNewRoute(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+GetTickCount(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUSHORT HopCount
+ );
+
+#ifndef QFE_BUILD
+
+NTSTATUS
+SubmitLineChangeRequest(
+ VOID
+ );
+
+#endif
+
+VOID
+FspProcessLineChange(
+ IN PVOID Context
+ );
+
+// Lock.c
+
+NTSTATUS
+NwFsdLockControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+NwFreeLocksForIcb(
+ PIRP_CONTEXT pIrpContext,
+ PICB Icb
+ );
+
+// Lockcode.c
+
+VOID
+NwReferenceUnlockableCodeSection (
+ VOID
+ );
+
+VOID
+NwDereferenceUnlockableCodeSection (
+ VOID
+ );
+
+BOOLEAN
+NwUnlockCodeSections(
+ BOOLEAN BlockIndefinitely
+ );
+
+// Pid.c
+
+BOOLEAN
+NwInitializePidTable(
+ VOID
+ );
+
+NTSTATUS
+NwMapPid(
+ IN ULONG Pid32,
+ OUT PUCHAR Pid8
+ );
+
+VOID
+NwSetEndOfJobRequired(
+ IN UCHAR Pid8
+ );
+
+VOID
+NwUnmapPid(
+ IN UCHAR Pid8,
+ IN PIRP_CONTEXT IrpContext OPTIONAL
+ );
+
+VOID
+NwUninitializePidTable(
+ VOID
+ );
+
+// Read.c
+
+NTSTATUS
+NwFsdRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+BurstReadTimeout(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ResubmitBurstRead (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+#if NWFASTIO
+BOOLEAN
+NwFastRead (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+#endif
+
+// Scavenger.c
+
+VOID
+DisconnectTimedOutScbs(
+ LARGE_INTEGER Now
+ );
+
+VOID
+NwScavengerRoutine(
+ IN PWORK_QUEUE_ITEM WorkItem
+ );
+
+BOOLEAN
+NwAllocateExtraIrpContext(
+ OUT PIRP_CONTEXT *ppIrpContext,
+ IN PNONPAGED_SCB pScb
+ );
+
+VOID
+NwFreeExtraIrpContext(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+CleanupScbs(
+ LARGE_INTEGER Now
+ );
+
+// Security.c
+
+VOID
+CreateAnsiUid(
+ OUT PCHAR aUid,
+ IN PLARGE_INTEGER Uid
+ );
+
+NTSTATUS
+MakeUidServer(
+ PUNICODE_STRING UidServer,
+ PLARGE_INTEGER Uid,
+ PUNICODE_STRING Server
+ );
+
+NTSTATUS
+Logon(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreeLogon(
+ IN PLOGON Logon
+ );
+
+NTSTATUS
+Logoff(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UpdateUsersPassword(
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ OUT PLARGE_INTEGER Uid
+ );
+
+NTSTATUS
+UpdateServerPassword(
+ PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING ServerName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN PLARGE_INTEGER Uid
+ );
+
+// String.c
+
+NTSTATUS
+DuplicateStringWithString (
+ OUT PSTRING DestinationString,
+ IN PSTRING SourceString,
+ IN POOL_TYPE PoolType
+ );
+
+
+NTSTATUS
+DuplicateUnicodeStringWithString (
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN POOL_TYPE PoolType
+ );
+
+NTSTATUS
+SetUnicodeString (
+ IN PUNICODE_STRING Destination,
+ IN ULONG Length,
+ IN PWCHAR Source
+ );
+
+VOID
+MergeStrings(
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING S1,
+ IN PUNICODE_STRING S2,
+ IN ULONG Type
+ );
+
+// Strucsup.c
+
+VOID
+NwInitializeRcb (
+ IN PRCB Rcb
+ );
+
+VOID
+NwDeleteRcb (
+ IN PRCB Rcb
+ );
+
+PFCB
+NwCreateFcb (
+ IN PUNICODE_STRING FileName,
+ IN PSCB Scb,
+ IN PVCB Vcb
+ );
+
+PFCB
+NwFindFcb (
+ IN PSCB Scb,
+ IN PVCB Vcb,
+ IN PUNICODE_STRING FileName,
+ IN PDCB Dcb OPTIONAL
+ );
+
+VOID
+NwDereferenceFcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PFCB Fcb
+ );
+
+PICB
+NwCreateIcb (
+ IN USHORT Type,
+ IN PVOID Associate
+ );
+
+VOID
+NwVerifyIcb (
+ IN PICB Icb
+ );
+
+VOID
+NwVerifyIcbSpecial(
+ IN PICB Icb
+ );
+
+VOID
+NwVerifyScb (
+ IN PSCB Scb
+ );
+
+VOID
+NwDeleteIcb (
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PICB Icb
+ );
+
+PVCB
+NwFindVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection,
+ IN BOOLEAN FindExisting
+ );
+
+PVCB
+NwCreateVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection
+ );
+
+VOID
+NwDereferenceVcb (
+ IN PVCB Vcb,
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN BOOLEAN OwnRcb
+ );
+
+VOID
+NwCleanupVcb(
+ IN PVCB pVcb,
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+NwCloseAllVcbs(
+ PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+NwReopenVcbHandlesForScb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ );
+
+VOID
+NwReopenVcbHandle(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb
+ );
+
+ULONG
+NwInvalidateAllHandles (
+ PLARGE_INTEGER Uid,
+ PIRP_CONTEXT IrpContext
+ );
+
+ULONG
+NwInvalidateAllHandlesForScb (
+ PSCB Scb
+ );
+
+BOOLEAN
+IsFatNameValid (
+ IN PUNICODE_STRING FileName
+ );
+
+// Timer.c
+
+VOID
+StartTimer(
+ );
+
+VOID
+StopTimer(
+ );
+
+// Util.c
+
+VOID
+CopyBufferToMdl(
+ PMDL DestinationMdl,
+ ULONG DataOffset,
+ PVOID SourceData,
+ ULONG SourceByteCount
+ );
+
+NTSTATUS
+GetCredentialFromServerName(
+ IN PUNICODE_STRING puServerName,
+ OUT PUNICODE_STRING puCredentialName
+);
+
+NTSTATUS
+BuildExCredentialServerName(
+ IN PUNICODE_STRING puServerName,
+ IN PUNICODE_STRING puUserName,
+ OUT PUNICODE_STRING puExCredServerName
+);
+
+NTSTATUS
+UnmungeCredentialName(
+ IN PUNICODE_STRING puCredName,
+ OUT PUNICODE_STRING puServerName
+);
+
+BOOLEAN
+IsCredentialName(
+ IN PUNICODE_STRING puObjectName
+);
+
+// VolInfo.c
+
+NTSTATUS
+NwFsdQueryVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwFsdSetVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// WorkQue.c
+
+PIRP_CONTEXT
+AllocateIrpContext (
+ PIRP pIrp
+ );
+
+VOID
+FreeIrpContext (
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+InitializeIrpContext (
+ VOID
+ );
+
+VOID
+UninitializeIrpContext (
+ VOID
+ );
+
+VOID
+NwCompleteRequest (
+ PIRP_CONTEXT IrpContext,
+ NTSTATUS Status
+ );
+
+VOID
+NwAppendToQueueAndWait(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+NwDequeueIrpContext(
+ IN PIRP_CONTEXT IrpContext,
+ IN BOOLEAN OwnSpinLock
+ );
+
+VOID
+NwCancelIrp (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+PIRP
+NwAllocateSendIrp (
+ PIRP_CONTEXT IrpContext
+ );
+
+PMINI_IRP_CONTEXT
+AllocateMiniIrpContext (
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreeMiniIrpContext (
+ PMINI_IRP_CONTEXT MiniIrpContext
+ );
+
+// Write.c
+
+NTSTATUS
+NwFsdWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DoWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+NwFsdFlushBuffers(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ResubmitBurstWrite(
+ PIRP_CONTEXT IrpContext
+ );
+
+#if NWFASTIO
+BOOLEAN
+NwFastWrite (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+#endif
+
+//
+// A function that returns finished denotes if it was able to complete the
+// operation (TRUE) or could not complete the operation (FALSE) because the
+// wait value stored in the irp context was false and we would have had
+// to block for a resource or I/O
+//
+
+typedef BOOLEAN FINISHED;
+
+//
+// Miscellaneous support routines
+//
+
+//
+// This macro returns TRUE if a flag in a set of flags is on and FALSE
+// otherwise. It is followed by two macros for setting and clearing
+// flags
+//
+
+#define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0)))
+
+#define SetFlag(Flags,SingleFlag) { \
+ (Flags) |= (SingleFlag); \
+}
+
+
+#define ClearFlag(Flags,SingleFlag) { \
+ (Flags) &= ~(SingleFlag); \
+}
+
+//
+// The following macro is used to determine if an FSD thread can block
+// for I/O or wait for a resource. It returns TRUE if the thread can
+// block and FALSE otherwise. This attribute can then be used to call
+// the FSD & FSP common work routine with the proper wait value.
+//
+
+#define CanFsdWait(IRP) IoIsOperationSynchronous(IRP)
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up word
+// value
+//
+
+#define WordAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \
+ )
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up longword
+// value
+//
+
+#define LongAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \
+ )
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up quadword
+// value
+//
+
+#define QuadAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \
+ )
+
+//
+// The following two macro are used by the Fsd/Fsp exception handlers to
+// process an exception. The first macro is the exception filter used in the
+// Fsd/Fsp to decide if an exception should be handled at this level.
+// The second macro decides if the exception is to be finished off by
+// completing the IRP, and cleaning up the Irp Context, or if we should
+// bugcheck. Exception values such as STATUS_FILE_INVALID (raised by
+// VerfySup.c) cause us to complete the Irp and cleanup, while exceptions
+// such as accvio cause us to bugcheck.
+//
+// The basic structure for fsd/fsp exception handling is as follows:
+//
+// NwFsdXxx(...)
+// {
+// try {
+//
+// ...
+//
+// } except(NwExceptionFilter( IrpContext, GetExceptionCode() )) {
+//
+// Status = NwProcessException( IrpContext, Irp, GetExceptionCode() );
+// }
+//
+// Return Status;
+// }
+//
+// To explicitly raise an exception that we expect, such as
+// STATUS_FILE_INVALID, use the below macro NwRaiseStatus(). To raise a
+// status from an unknown origin (such as CcFlushCache()), use the macro
+// NwNormalizeAndRaiseStatus. This will raise the status if it is expected,
+// or raise STATUS_UNEXPECTED_IO_ERROR if it is not.
+//
+// Note that when using these two macros, the original status is placed in
+// IrpContext->ExceptionStatus, signaling NwExceptionFilter and
+// NwProcessException that the status we actually raise is by definition
+// expected.
+//
+
+LONG
+NwExceptionFilter (
+ IN PIRP Irp,
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ );
+
+NTSTATUS
+NwProcessException (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS ExceptionCode
+ );
+
+//
+// VOID
+// NwRaiseStatus (
+// IN NT_STATUS Status
+// );
+//
+//
+
+#define NwRaiseStatus(IRPCONTEXT,STATUS) { \
+ ExRaiseStatus( (STATUS) ); \
+ KeBugCheck( NW_FILE_SYSTEM ); \
+}
+
+//
+// VOID
+// NwNormalAndRaiseStatus (
+// IN NT_STATUS Status
+// );
+//
+
+#define NwNormalizeAndRaiseStatus(IRPCONTEXT,STATUS) { \
+ if ((STATUS) == STATUS_VERIFY_REQUIRED) { ExRaiseStatus((STATUS)); } \
+ ExRaiseStatus(FsRtlNormalizeNtstatus((STATUS),STATUS_UNEXPECTED_IO_ERROR)); \
+ KeBugCheck( NW_FILE_SYSTEM ); \
+}
+
+//
+// The Following routine makes a popup
+//
+
+#define NwRaiseInformationalHardError(STATUS,NAME) { \
+ UNICODE_STRING Name; \
+ if (NT_SUCCESS(RtlOemStringToCountedUnicodeString(&Name, (NAME), TRUE))) { \
+ IoRaiseInformationalHardError(Status, &Name, (Irp == NULL ?\
+ NULL : &(Irp->Tail.Overlay.Thread)->Tcb)); \
+ RtlFreeUnicodeString(&Name); \
+ } \
+}
+
+
+//
+// The following macros are used to establish the semantics needed
+// to do a return from within a try-finally clause. As a rule every
+// try clause must end with a label call try_exit. For example,
+//
+// try {
+// :
+// :
+//
+// try_exit: NOTHING;
+// } finally {
+//
+// :
+// :
+// }
+//
+// Every return statement executed inside of a try clause should use the
+// try_return macro. If the compiler fully supports the try-finally construct
+// then the macro should be
+//
+// #define try_return(S) { return(S); }
+//
+// If the compiler does not support the try-finally construct then the macro
+// should be
+//
+// #define try_return(S) { S; goto try_exit; }
+//
+
+#define try_return(S) { S; goto try_exit; }
+
+
+#if NWDBG
+#define InternalError(String) { \
+ DbgPrint("Internal NetWare Redirector Error "); \
+ DbgPrint String; \
+ DbgPrint("\nFile %s, Line %d\n", __FILE__, __LINE__); \
+ ASSERT(FALSE); \
+}
+#else
+#define InternalError(String) {NOTHING;}
+#endif
+
+#define DbgPrintf DbgPrint
+
+//
+// Reference and dereference Macros.
+//
+
+VOID
+RefDbgTrace (
+ PVOID Resource,
+ DWORD Count,
+ BOOLEAN Reference,
+ PBYTE FileName,
+ UINT Line
+);
+
+#ifdef NWDBG
+
+VOID
+ChkNwReferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+);
+
+VOID
+ChkNwDereferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+);
+
+#define NwReferenceScb( pNpScb ) \
+ ChkNwReferenceScb( pNpScb, __FILE__, __LINE__, FALSE )
+
+#define NwQuietReferenceScb( pNpScb ) \
+ ChkNwReferenceScb( pNpScb, __FILE__, __LINE__, TRUE )
+
+#define NwDereferenceScb( pNpScb ) \
+ ChkNwDereferenceScb( pNpScb, __FILE__, __LINE__, FALSE )
+
+#define NwQuietDereferenceScb( pNpScb ) \
+ ChkNwDereferenceScb( pNpScb, __FILE__, __LINE__, TRUE )
+
+#else
+
+#define NwReferenceScb( pNpScb ) \
+ ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+
+#define NwQuietReferenceScb( pNpScb ) \
+ ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+
+#define NwDereferenceScb( pNpScb ) \
+ ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+
+#define NwQuietDereferenceScb( pNpScb ) \
+ ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+#endif
+
+//
+// Irpcontext event macro.
+//
+
+#define NwSetIrpContextEvent( pIrpContext ) \
+ DebugTrace( 0, DEBUG_TRACE_WORKQUE, "Set event for IrpC = %08lx\n", pIrpContext ); \
+ DebugTrace( 0, DEBUG_TRACE_WORKQUE, "IrpC->pNpScb = %08lx\n", pIrpContext->pNpScb ); \
+ KeSetEvent( &pIrpContext->Event, 0, FALSE )
+
+//
+// VCB macros must be called with the RCB resource held.
+//
+
+
+#if NWDBG
+VOID
+NwReferenceVcb (
+ IN PVCB Vcb
+ );
+#else
+#define NwReferenceVcb( pVcb ) ++(pVcb)->Reference;
+#endif
+
+//
+// Resource acquisition and release macros
+//
+
+#if NWDBG
+
+VOID
+NwAcquireExclusiveRcb(
+ PRCB Rcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwAcquireSharedRcb(
+ PRCB Rcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwReleaseRcb(
+ PRCB Rcb
+ );
+
+VOID
+NwAcquireExclusiveFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwAcquireSharedFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwReleaseFcb(
+ PNONPAGED_FCB pFcb
+ );
+
+VOID
+NwAcquireOpenLock(
+ VOID
+ );
+
+VOID
+NwReleaseOpenLock(
+ VOID
+ );
+
+#else
+
+#define NwAcquireExclusiveRcb( Rcb, Wait ) \
+ ExAcquireResourceExclusive( &((Rcb)->Resource), Wait )
+
+#define NwAcquireSharedRcb( Rcb, Wait ) \
+ ExAcquireResourceShared( &((Rcb)->Resource), Wait )
+
+#define NwReleaseRcb( Rcb ) \
+ ExReleaseResource( &((Rcb)->Resource) )
+
+#define NwAcquireExclusiveFcb( pFcb, Wait ) \
+ ExAcquireResourceExclusive( &((pFcb)->Resource), Wait )
+
+#define NwAcquireSharedFcb( pFcb, Wait ) \
+ ExAcquireResourceShared( &((pFcb)->Resource), Wait )
+
+#define NwReleaseFcb( pFcb ) \
+ ExReleaseResource( &((pFcb)->Resource) )
+
+#define NwAcquireOpenLock( ) \
+ ExAcquireResourceExclusive( &NwOpenResource, TRUE )
+
+#define NwReleaseOpenLock( ) \
+ ExReleaseResource( &NwOpenResource )
+
+#endif
+
+#define NwReleaseFcbForThread( pFcb, pThread ) \
+ ExReleaseResourceForThread( &((pFcb)->Resource), pThread )
+
+//
+// Memory allocation and deallocation macros
+//
+
+#ifdef NWDBG
+
+#define ALLOCATE_POOL_EX( Type, Size ) NwAllocatePool( Type, Size, TRUE )
+#define ALLOCATE_POOL( Type, Size ) NwAllocatePool( Type, Size, FALSE )
+#define FREE_POOL( Buffer ) NwFreePool( Buffer )
+
+#define ALLOCATE_IRP( Size, ChargeQuota ) \
+ NwAllocateIrp( Size, ChargeQuota )
+#define FREE_IRP( Irp ) NwFreeIrp( Irp )
+
+#define ALLOCATE_MDL( Va, Length, Secondary, ChargeQuota, Irp ) \
+ NwAllocateMdl(Va, Length, Secondary, ChargeQuota, Irp, __FILE__, __LINE__ )
+#define FREE_MDL( Mdl ) NwFreeMdl( Mdl )
+
+#else
+
+#define ALLOCATE_POOL_EX( Type, Size ) FsRtlAllocatePool( Type, Size )
+#ifndef QFE_BUILD
+#define ALLOCATE_POOL( Type, Size ) ExAllocatePoolWithTag( Type, Size, 'scwn' )
+#else
+#define ALLOCATE_POOL( Type, Size ) ExAllocatePool( Type, Size )
+#endif
+#define FREE_POOL( Buffer ) ExFreePool( Buffer )
+
+#define ALLOCATE_IRP( Size, ChargeQuota ) \
+ IoAllocateIrp( Size, ChargeQuota )
+#define FREE_IRP( Irp ) IoFreeIrp( Irp )
+
+#define ALLOCATE_MDL( Va, Length, Secondary, ChargeQuota, Irp ) \
+ IoAllocateMdl(Va, Length, Secondary, ChargeQuota, Irp )
+#define FREE_MDL( Mdl ) IoFreeMdl( Mdl )
+#endif
+
+//
+// Useful macros
+//
+
+#define MIN(a,b) ((a)<(b) ? (a):(b))
+#define MAX(a,b) ((a)>(b) ? (a):(b))
+
+#define DIFFERENT_PAGES( START, SIZE ) \
+ (((ULONG)START & ~(4096-1)) != (((ULONG)START + SIZE) & ~(4096-1)))
+
+#define UP_LEVEL_SERVER( Scb ) \
+ ( ( Scb->MajorVersion >= 4 ) || \
+ ( Scb->MajorVersion == 3 && Scb->MinorVersion >= 12 ) )
+
+#define LFN_SUPPORTED( Scb ) \
+ ( ( Scb->MajorVersion >= 4 ) || \
+ ( Scb->MajorVersion == 3 && Scb->MinorVersion >= 11 ) )
+
+#define LongByteSwap( l1, l2 ) \
+{ \
+ PUCHAR c1 = (PUCHAR)&l1; \
+ PUCHAR c2 = (PUCHAR)&l2; \
+ c1[0] = c2[3]; \
+ c1[1] = c2[2]; \
+ c1[2] = c2[1]; \
+ c1[3] = c2[0]; \
+}
+
+#define ShortByteSwap( s1, s2 ) \
+{ \
+ PUCHAR c1 = (PUCHAR)&s1; \
+ PUCHAR c2 = (PUCHAR)&s2; \
+ c1[0] = c2[1]; \
+ c1[1] = c2[0]; \
+}
+
+
+
+#define CanLogTimeOutEvent( LastTime, CurrentTime ) \
+ ( ( CurrentTime.QuadPart ) - ( LastTime.QuadPart ) >= 0 )
+
+#define UpdateNextEventTime( LastTime, CurrentTime, TimeOutEventInterval ) \
+ ( LastTime.QuadPart ) = ( CurrentTime.QuadPart ) + \
+ ( TimeOutEventInterval.QuadPart )
+
+
+
+//
+// Macros to isolate NT 3.1 and NT 3.5 differences.
+//
+
+#ifdef QFE_BUILD
+
+#define NwGetTopLevelIrp() (PIRP)(PsGetCurrentThread()->TopLevelIrp)
+#define NwSetTopLevelIrp(Irp) (PIRP)(PsGetCurrentThread())->TopLevelIrp = Irp;
+
+
+#else
+
+#define NwGetTopLevelIrp() IoGetTopLevelIrp()
+#define NwSetTopLevelIrp(Irp) IoSetTopLevelIrp(Irp)
+
+#endif
+
+#endif // _NWPROCS_
diff --git a/private/nw/rdr/read.c b/private/nw/rdr/read.c
new file mode 100644
index 000000000..de5e63f03
--- /dev/null
+++ b/private/nw/rdr/read.c
@@ -0,0 +1,2838 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Read.c
+
+Abstract:
+
+ This module implements support for NtReadFile for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 07-Apr-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#ifdef NWDBG
+#include <stdlib.h> // rand()
+#endif
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_READ)
+
+#define SIZE_ADJUST( ic ) \
+ ( sizeof( ULONG ) + sizeof( ULONG ) + ( ic->Specific.Read.FileOffset & 0x03 ) )
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonRead (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ReadNcp(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ReadNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+BuildReadNcp(
+ PIRP_CONTEXT IrpContext,
+ ULONG FileOffset,
+ USHORT Length
+ );
+
+NTSTATUS
+ParseReadResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_READ_RESPONSE Response,
+ ULONG BytesAvailable,
+ PUSHORT Length
+ );
+
+NTSTATUS
+BurstRead(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+BuildBurstReadRequest(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG Handle,
+ IN ULONG FileOffset,
+ IN ULONG Length
+ );
+
+NTSTATUS
+BurstReadCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+RecordPacketReceipt(
+ IN OUT PIRP_CONTEXT IrpContext,
+ IN PVOID ReadData,
+ IN ULONG DataOffset,
+ IN USHORT BytesCount,
+ IN BOOLEAN CopyData
+ );
+
+BOOLEAN
+VerifyBurstRead(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreePacketList(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BurstReadReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+NTSTATUS
+ReadNcpReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+NTSTATUS
+ParseBurstReadResponse(
+ IN PIRP_CONTEXT IrpContext,
+ PVOID Response,
+ ULONG BytesAvailable,
+ PUCHAR Flags,
+ PULONG DataOffset,
+ PUSHORT BytesThisPacket,
+ PUCHAR *ReadData,
+ PULONG TotalBytesRead
+ );
+
+PMDL
+AllocateReceivePartialMdl(
+ PMDL FullMdl,
+ ULONG DataOffset,
+ ULONG BytesThisPacket
+ );
+
+VOID
+SetConnectionTimeout(
+ PNONPAGED_SCB pNpScb,
+ ULONG Length
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdRead )
+#pragma alloc_text( PAGE, NwCommonRead )
+#pragma alloc_text( PAGE, ReadNcp )
+#pragma alloc_text( PAGE, BurstRead )
+#pragma alloc_text( PAGE, BuildBurstReadRequest )
+#pragma alloc_text( PAGE, ResubmitBurstRead )
+#pragma alloc_text( PAGE, SetConnectionTimeout )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, ReadNcpCallback )
+#pragma alloc_text( PAGE1, ReadNcpReceive )
+#pragma alloc_text( PAGE1, BuildReadNcp )
+#pragma alloc_text( PAGE1, ParseReadResponse )
+#pragma alloc_text( PAGE1, BurstReadCallback )
+#pragma alloc_text( PAGE1, BurstReadTimeout )
+#pragma alloc_text( PAGE1, RecordPacketReceipt )
+#pragma alloc_text( PAGE1, VerifyBurstRead )
+#pragma alloc_text( PAGE1, FreePacketList )
+#pragma alloc_text( PAGE1, BurstReadReceive )
+#pragma alloc_text( PAGE1, ParseBurstReadResponse )
+#pragma alloc_text( PAGE1, AllocateReceivePartialMdl )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtReadFile.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the read function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdRead\n", 0);
+
+ //
+ // Call the common direcotry control routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonRead( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdRead -> %08lx\n", status );
+
+ Stats.ReadOperations++;
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonRead (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtReadFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ ULONG BufferLength; // Size application requested to read
+ ULONG ByteOffset;
+ ULONG PreviousByteOffset;
+ ULONG BytesRead;
+ ULONG NewBufferLength;
+ PVOID SystemBuffer;
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonRead...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+
+ if (((nodeTypeCode != NW_NTC_ICB) &&
+ (nodeTypeCode != NW_NTC_ICB_SCB)) ||
+ (!icb->HasRemoteHandle) ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( icb );
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ IrpContext->pScb = icb->SuperType.Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ fcb = NULL;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status );
+ return status;
+ }
+
+ BufferLength = irpSp->Parameters.Read.Length;
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+
+ //
+ // Fail reads beyond file offset 4GB.
+ //
+
+ if ( irpSp->Parameters.Read.ByteOffset.HighPart != 0 ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // Special case 0 length read.
+ //
+
+ if ( BufferLength == 0 ) {
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+ }
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ PreviousByteOffset = irpSp->FileObject->CurrentByteOffset.LowPart;
+ irpSp->FileObject->CurrentByteOffset.LowPart = ByteOffset;
+ }
+
+ //
+ // First flush the write behind cache unless this is a
+ // file stream operation.
+ //
+
+ if ( fcb ) {
+
+ status = AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ if ( !NT_SUCCESS( status ) ) {
+ goto ResetByteOffsetAndExit;
+ }
+
+ //
+ // Read as much as we can from cache.
+ //
+
+ NwMapUserBuffer( Irp, KernelMode, &SystemBuffer );
+
+ BytesRead = CacheRead(
+ fcb->NonPagedFcb,
+ ByteOffset,
+ BufferLength,
+#if NWFASTIO
+ SystemBuffer,
+ FALSE );
+#else
+ SystemBuffer );
+#endif
+
+ //
+ // If all the data was the the cache, we are done.
+ //
+
+ if ( BytesRead == BufferLength ) {
+
+ Irp->IoStatus.Information = BytesRead;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BytesRead;
+ }
+
+ //
+ // If this is a paging read, we need to flush the MDL
+ // since on some systems the I-cache and D-cache
+ // are not synchronized.
+ //
+
+ if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+ KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE);
+ }
+
+ //
+ // Record read offset and size to discover a sequential read pattern.
+ //
+
+ fcb->LastReadOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Read.Length;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ // Protect read cache
+ NwAcquireExclusiveFcb( fcb->NonPagedFcb, TRUE );
+
+ IrpContext->Specific.Read.CacheReadSize = BytesRead;
+ fcb->NonPagedFcb->CacheFileOffset = ByteOffset + BufferLength;
+
+ ByteOffset += BytesRead;
+ BufferLength -= BytesRead;
+
+ NewBufferLength = CalculateReadAheadSize(
+ IrpContext,
+ fcb->NonPagedFcb,
+ BytesRead,
+ ByteOffset,
+ BufferLength );
+
+ IrpContext->Specific.Read.ReadAheadSize = NewBufferLength - BufferLength;
+
+ } else {
+
+ //
+ // This is a read from a ds file stream handle. For now,
+ // there's no cache support.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ BytesRead = 0;
+
+ IrpContext->Specific.Read.CacheReadSize = BytesRead;
+ IrpContext->Specific.Read.ReadAheadSize = 0;
+ }
+
+ //
+ // If burst mode is enabled, and this read is too big to do in a single
+ // core read NCP, use burst mode.
+ //
+ // BUGBUG: We don't support burst against a ds file stream yet.
+ //
+
+ if ( IrpContext->pNpScb->ReceiveBurstModeEnabled &&
+ NewBufferLength > IrpContext->pNpScb->BufferSize &&
+ fcb ) {
+ status = BurstRead( IrpContext );
+ } else {
+ status = ReadNcp( IrpContext );
+ }
+
+ Irp->MdlAddress = IrpContext->pOriginalMdlAddress;
+
+ if (Irp->MdlAddress != NULL) {
+ // Next might point to the cache mdl.
+ Irp->MdlAddress->Next = NULL;
+ }
+
+ if ( NT_SUCCESS( status ) ) {
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += Irp->IoStatus.Information;
+ }
+
+ //
+ // If this is a paging read, we need to flush the MDL
+ // since on some systems the I-cache and D-cache
+ // are not synchronized.
+ //
+
+ if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+ KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE);
+ }
+
+ //
+ // If we received 0 bytes without an error, we must be beyond
+ // the end of file.
+ //
+
+ if ( Irp->IoStatus.Information == 0 ) {
+ status = STATUS_END_OF_FILE;
+ }
+ }
+
+ //
+ // Record read offset and size to discover a sequential read pattern.
+ //
+
+ if ( fcb ) {
+
+ fcb->LastReadOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Read.Length;
+
+ NwReleaseFcb( fcb->NonPagedFcb );
+
+ }
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status);
+
+ResetByteOffsetAndExit:
+
+ if ( !NT_SUCCESS( status ) ) {
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.LowPart = PreviousByteOffset;
+
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+ReadNcp(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of read NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ PMDL DataMdl;
+
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PICB Icb;
+ ULONG ByteOffset;
+ ULONG BufferLength;
+ ULONG MdlLength;
+ BOOLEAN Done;
+ PMDL Mdl, NextMdl;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+ Icb = IrpContext->Icb;
+
+ BufferLength = irpSp->Parameters.Read.Length +
+ IrpContext->Specific.Read.ReadAheadSize -
+ IrpContext->Specific.Read.CacheReadSize;
+
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart +
+ IrpContext->Specific.Read.CacheReadSize;
+
+ IrpContext->Specific.Read.FileOffset = ByteOffset;
+
+ DebugTrace(+1, Dbg, "ReadNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "Length = %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "Offset = %d\n", ByteOffset);
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ } else if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT( pScb );
+
+ //
+ // Update the original MDL record in the Irp context so that we
+ // can restore it on i/o completion.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ Length = MIN( IrpContext->pNpScb->BufferSize, BufferLength );
+
+ //
+ // The old servers will not accept reads that cross 4k boundaries in the file
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( ByteOffset, Length ))) {
+
+ Length = 4096 - ((ULONG)ByteOffset & (4096-1));
+
+ }
+
+ IrpContext->Specific.Read.Buffer = irp->UserBuffer;
+ IrpContext->Specific.Read.ReadOffset = IrpContext->Specific.Read.CacheReadSize;
+ IrpContext->Specific.Read.RemainingLength = BufferLength;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ //
+ // Set up to process a read NCP
+ //
+
+ pNpScb = pScb->pNpScb;
+ IrpContext->pEx = ReadNcpCallback;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+ IrpContext->PacketType = NCP_FUNCTION;
+ IrpContext->ReceiveDataRoutine = ReadNcpReceive;
+
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+ pNpScb->RetryCount = DefaultRetryCount;
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Setup to do at most 64K of i/o asynchronously, or buffer length.
+ //
+
+ IrpContext->Specific.Read.BurstSize =
+ MIN( 64 * 1024, IrpContext->Specific.Read.RemainingLength );
+
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+
+ //
+ // Try to allocate an MDL for this i/o.
+ //
+
+ if ( IrpContext->Specific.Read.ReadAheadSize == 0 ) {
+ MdlLength = IrpContext->Specific.Read.BurstSize;
+ } else {
+ MdlLength = IrpContext->Specific.Read.BurstSize - IrpContext->Specific.Read.ReadAheadSize;
+ }
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpContext->Specific.Read.FullMdl = DataMdl;
+
+ //
+ // If there is no MDL for this read, probe the data MDL to
+ // lock it's pages down. Otherwise, use the data MDL as
+ // a partial MDL.
+ //
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoWriteAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ IrpContext->pOriginalMdlAddress,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength );
+
+ }
+
+ IrpContext->Specific.Read.BurstBuffer = MmGetSystemAddressForMdl( DataMdl );
+
+ if ( IrpContext->Specific.Read.BurstSize ==
+ IrpContext->Specific.Read.RemainingLength ) {
+ Done = TRUE;
+ }
+
+ if ( IrpContext->Specific.Read.ReadAheadSize != 0 ) {
+ DataMdl->Next = Icb->NpFcb->CacheMdl;
+ }
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+
+ //
+ // Build and send the request.
+ //
+
+ BuildReadNcp(
+ IrpContext,
+ IrpContext->Specific.Read.FileOffset,
+ (USHORT) MIN( Length, IrpContext->Specific.Read.RemainingLength ) );
+
+ status = PrepareAndSendPacket( IrpContext );
+ if ( NT_SUCCESS( status )) {
+ KeWaitForSingleObject(
+ &IrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ status = IrpContext->Specific.Read.Status;
+
+ }
+
+ //
+ // Stop looping if the read failed, or we read less data than
+ // requested.
+ //
+
+ if ( !NT_SUCCESS( status ) ||
+ IrpContext->Specific.Read.BurstSize != 0 ) {
+
+ Done = TRUE;
+
+ }
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ }
+
+ //
+ // Free the receive MDL if one was allocated.
+ //
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ DebugTrace(-1, Dbg, "ReadNcp -> %08lx\n", status );
+
+ Stats.ReadNcps++;
+
+ return status;
+}
+
+
+NTSTATUS
+ReadNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BytesAvailable - Number of bytes in the response.
+
+ Response - The response data.
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length;
+ USHORT USLength;
+ PNONPAGED_FCB NpFcb;
+
+ DebugTrace(0, Dbg, "ReadNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_REMOTE_NOT_LISTENING;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // How much data was received?
+ //
+
+ Status = ParseReadResponse(
+ IrpContext,
+ (PNCP_READ_RESPONSE)Response,
+ BytesAvailable,
+ &USLength );
+
+ Length = (ULONG)USLength;
+ DebugTrace(0, Dbg, "Ncp contains %d bytes\n", Length);
+
+ if ((NT_SUCCESS(Status)) &&
+ (Length != 0)) {
+
+ //
+ // If we are receiving the data at indication time, copy the
+ // user's data to the user's buffer.
+ //
+
+ if ( Response != IrpContext->rsp ) {
+
+ //
+ // Read in the data.
+ // Note: If the FileOffset is at an odd byte then the server
+ // will insert an extra pad byte.
+ //
+
+ CopyBufferToMdl(
+ IrpContext->Specific.Read.FullMdl,
+ IrpContext->Specific.Read.BurstRequestOffset,
+ Response + sizeof( NCP_READ_RESPONSE ) + ( IrpContext->Specific.Read.FileOffset & 1),
+ Length );
+
+ DebugTrace( 0, Dbg, "RxLength= %ld\n", Length);
+
+ dump( Dbg,(PUCHAR)IrpContext->Specific.Read.BurstBuffer +
+ IrpContext->Specific.Read.BurstRequestOffset,
+ Length);
+
+ }
+
+ DebugTrace( 0, Dbg, "RxLength= %ld\n", Length);
+ IrpContext->Specific.Read.ReadOffset += Length;
+ IrpContext->Specific.Read.BurstRequestOffset += Length;
+ IrpContext->Specific.Read.FileOffset += Length;
+ IrpContext->Specific.Read.RemainingLength -= Length;
+ IrpContext->Specific.Read.BurstSize -= Length;
+ }
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n",IrpContext->Specific.Read.RemainingLength);
+
+ //
+ // If the previous read was succesful, and we received as much data
+ // as we asked for, and there is more locked data, send the next
+ // read request.
+ //
+
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( IrpContext->Specific.Read.BurstSize != 0 ) &&
+ ( Length == IrpContext->Specific.Read.LastReadLength ) ) {
+
+ //
+ // Read the next packet.
+ //
+
+ Length = MIN( IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Read.BurstSize );
+
+ //
+ // The server will not accept reads that cross 4k boundaries
+ // in the file.
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( IrpContext->Specific.Read.FileOffset, Length ))) {
+ Length = 4096 - ((ULONG)IrpContext->Specific.Read.FileOffset & (4096-1));
+ }
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ //
+ // Build and send the request.
+ //
+
+ BuildReadNcp(
+ IrpContext,
+ IrpContext->Specific.Read.FileOffset,
+ (USHORT)Length );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ Stats.ReadNcps++;
+
+ if ( !NT_SUCCESS(Status) ) {
+ // Abandon this request
+ goto returnstatus;
+ }
+
+ } else {
+returnstatus:
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+ NpFcb = IrpContext->Icb->NpFcb;
+
+ if ( IrpContext->Icb->NodeTypeCode == NW_NTC_ICB_SCB ) {
+ NpFcb = NULL;
+ }
+
+ //
+ // Calculate how much data we read into the cache, and how much data
+ // we read into the users buffer.
+ //
+
+ if ( NpFcb ) {
+
+ if ( IrpContext->Specific.Read.ReadOffset > irpSp->Parameters.Read.Length ) {
+
+ ASSERT(NpFcb->CacheBuffer != NULL ) ; // had better be there..
+
+ NpFcb->CacheDataSize = IrpContext->Specific.Read.ReadOffset -
+ irpSp->Parameters.Read.Length;
+
+ Irp->IoStatus.Information = irpSp->Parameters.Read.Length;
+
+ } else {
+
+ NpFcb->CacheDataSize = 0;
+ Irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ } else {
+
+ Irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ //
+ // We're done with this request, signal the reading thread.
+ //
+
+ IrpContext->Specific.Read.Status = Status;
+
+ NwSetIrpContextEvent( IrpContext );
+ }
+
+ DebugTrace( 0, Dbg, "ReadNcpCallback -> %08lx\n", Status );
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+ReadNcpReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ )
+{
+ PMDL ReceiveMdl;
+ PMDL Mdl, NextMdl;
+
+ DebugTrace( 0, Dbg, "ReadNcpReceive\n", 0 );
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ //
+ // Set up receive MDL. Note that we get an extra byte of header
+ // when reading from an odd offset.
+ //
+
+ IrpContext->RxMdl->ByteCount = sizeof( NCP_READ_RESPONSE ) +
+ (IrpContext->Specific.Read.FileOffset & 1);
+
+ ASSERT( IrpContext->Specific.Read.FullMdl != NULL );
+
+ //
+ // If we are reading at EOF, or there was a read error there will
+ // be a small response.
+ //
+
+ if ( BytesAvailable > MmGetMdlByteCount( IrpContext->RxMdl ) ) {
+
+ ReceiveMdl = AllocateReceivePartialMdl(
+ IrpContext->Specific.Read.FullMdl,
+ IrpContext->Specific.Read.BurstRequestOffset,
+ BytesAvailable - MmGetMdlByteCount( IrpContext->RxMdl ) );
+
+ IrpContext->RxMdl->Next = ReceiveMdl;
+
+ // Record Mdl to free when CopyIndicatedData or Irp completed.
+ IrpContext->Specific.Read.PartialMdl = ReceiveMdl;
+
+ } else {
+
+ IrpContext->RxMdl->Next = NULL;
+
+ }
+
+ *pReceiveMdl = IrpContext->RxMdl;
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+BuildReadNcp(
+ PIRP_CONTEXT IrpContext,
+ ULONG FileOffset,
+ USHORT Length
+ )
+{
+ PNCP_READ_REQUEST ReadRequest;
+
+ ReadRequest = (PNCP_READ_REQUEST)IrpContext->req;
+
+ ReadRequest->RequestHeader.NcpHeader.Command = PEP_COMMAND_REQUEST;
+ ReadRequest->RequestHeader.NcpHeader.ConnectionIdLow =
+ IrpContext->pNpScb->ConnectionNo;
+ ReadRequest->RequestHeader.NcpHeader.ConnectionIdHigh =
+ IrpContext->pNpScb->ConnectionNoHigh;
+ ReadRequest->RequestHeader.NcpHeader.TaskId =
+ IrpContext->Icb->Pid;
+
+ ReadRequest->RequestHeader.FunctionCode = NCP_READ_FILE;
+ ReadRequest->Unused = 0;
+ RtlMoveMemory(
+ ReadRequest->Handle,
+ IrpContext->Icb->Handle,
+ sizeof( IrpContext->Icb->Handle ) );
+
+ LongByteSwap( ReadRequest->FileOffset, FileOffset );
+ ShortByteSwap( ReadRequest->Length, Length );
+
+ IrpContext->TxMdl->ByteCount = sizeof( *ReadRequest );
+ SetFlag( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ return;
+}
+
+NTSTATUS
+ParseReadResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_READ_RESPONSE Response,
+ ULONG BytesAvailable,
+ PUSHORT Length )
+{
+ NTSTATUS Status;
+
+ Status = ParseNcpResponse( IrpContext, &Response->ResponseHeader );
+
+ if (!NT_SUCCESS(Status)) {
+ return( Status );
+ }
+
+ if ( BytesAvailable < sizeof( NCP_READ_RESPONSE ) ) {
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+ }
+
+ ShortByteSwap( *Length, Response->Length );
+
+ return( Status );
+}
+
+NTSTATUS
+BurstRead(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of burst read NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ByteOffset - The file offset for the read.
+
+ BufferLength - The number of bytes to read.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ PMDL DataMdl;
+ ULONG MdlLength;
+
+ PSCB pScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PICB Icb;
+ PNONPAGED_SCB pNpScb;
+ ULONG ByteOffset;
+ ULONG BufferLength;
+
+ BOOLEAN Done;
+
+ PAGED_CODE();
+
+ pNpScb = IrpContext->pNpScb;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+ Icb = IrpContext->Icb;
+
+ BufferLength = irpSp->Parameters.Read.Length +
+ IrpContext->Specific.Read.ReadAheadSize -
+ IrpContext->Specific.Read.CacheReadSize;
+
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart +
+ IrpContext->Specific.Read.CacheReadSize;
+
+ IrpContext->Specific.Read.FileOffset = ByteOffset;
+ IrpContext->Specific.Read.TotalReadOffset = ByteOffset;
+ IrpContext->Specific.Read.TotalReadLength = BufferLength;
+
+ DebugTrace(+1, Dbg, "BurstRead...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "Length = %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "Offset = %ld\n", ByteOffset);
+ DebugTrace( 0, Dbg, "Org Len = %ld\n", irpSp->Parameters.Read.Length );
+ DebugTrace( 0, Dbg, "Org Off = %ld\n", irpSp->Parameters.Read.ByteOffset.LowPart );
+
+ ASSERT (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB);
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ //
+ // Update the original MDL record in the Irp context so that we
+ // can restore it on i/o completion.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ Length = MIN( pNpScb->MaxReceiveSize, BufferLength );
+
+ if ( pNpScb->BurstRenegotiateReqd ) {
+ pNpScb->BurstRenegotiateReqd = FALSE;
+
+ RenegotiateBurstMode( IrpContext, pNpScb );
+ }
+
+ IrpContext->Specific.Read.ReadOffset = IrpContext->Specific.Read.CacheReadSize;
+ IrpContext->Specific.Read.RemainingLength = BufferLength;
+ IrpContext->Specific.Read.LastReadLength = Length;
+
+ InitializeListHead( &IrpContext->Specific.Read.PacketList );
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ IrpContext->pTdiStruct = &pNpScb->Burst;
+ IrpContext->TimeoutRoutine = BurstReadTimeout;
+ IrpContext->ReceiveDataRoutine = BurstReadReceive;
+
+ IrpContext->Specific.Read.Buffer = irp->UserBuffer;
+
+ IrpContext->pEx = BurstReadCallback;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+ IrpContext->PacketType = NCP_BURST;
+
+ //
+ // Tell BurstWrite that it needs to send a dummy Ncp on the next write.
+ //
+
+ pNpScb->BurstDataWritten = 0x00010000;
+
+ //
+ // The server will pause NwReceiveDelay between packets. Make sure we have our timeout
+ // so that we will take that into account.
+ //
+
+ SetConnectionTimeout( IrpContext->pNpScb, Length );
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Set burst read timeouts to how long we think the burst should take.
+ //
+
+ pNpScb->RetryCount = 20;
+
+ //
+ // Allocate and build an MDL for the users buffer.
+ //
+
+ if ( IrpContext->Specific.Read.ReadAheadSize == 0 ) {
+ MdlLength = Length;
+ } else {
+ MdlLength = Length - IrpContext->Specific.Read.ReadAheadSize;
+ }
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If there is no MDL for this read, probe the data MDL to lock it's
+ // pages down.
+ //
+ // Otherwise, use the data MDL as a partial MDL and lock the pages
+ // accordingly.
+ //
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoWriteAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ IrpContext->pOriginalMdlAddress,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength );
+ }
+
+ IrpContext->Specific.Read.FullMdl = DataMdl;
+ IrpContext->Specific.Read.BurstBuffer = MmGetSystemAddressForMdl( DataMdl );
+
+ if ( IrpContext->Specific.Read.ReadAheadSize != 0 ) {
+ DataMdl->Next = Icb->NpFcb->CacheMdl;
+ }
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+
+ //
+ // Send the request.
+ //
+
+ BuildBurstReadRequest(
+ IrpContext,
+ *(ULONG UNALIGNED *)(&Icb->Handle[2]),
+ IrpContext->Specific.Read.FileOffset,
+ Length );
+
+ status = PrepareAndSendPacket( IrpContext );
+ if ( NT_SUCCESS( status )) {
+ status = KeWaitForSingleObject(
+ &IrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ FreePacketList( IrpContext );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST );
+
+ status = IrpContext->Specific.Read.Status;
+
+ if ( status != STATUS_REMOTE_NOT_LISTENING ) {
+ IrpContext->pNpScb->BurstRequestNo++;
+ NwProcessReceiveBurstSuccess( IrpContext->pNpScb );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ //
+ // Update the read status data.
+ //
+
+ IrpContext->Specific.Read.ReadOffset +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.FileOffset +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.RemainingLength -=
+ IrpContext->Specific.Read.BurstSize;
+
+ if ( IrpContext->Specific.Read.LastReadLength ==
+ IrpContext->Specific.Read.BurstSize &&
+
+ IrpContext->Specific.Read.RemainingLength > 0 ) {
+
+ //
+ // We've received all the data from the current burst, and we
+ // received as many bytes as we asked for, and we need more data
+ // to satisfy the users read request, start another read burst.
+ //
+
+ Length = MIN( IrpContext->pNpScb->MaxReceiveSize,
+ IrpContext->Specific.Read.RemainingLength );
+
+ DebugTrace( 0, Dbg, "Requesting another burst, length = %ld\n", Length);
+
+ ASSERT( Length != 0 );
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ (PUCHAR)IrpContext->Specific.Read.BurstBuffer +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ } else {
+ Done = TRUE;
+ }
+
+ }
+
+
+ //
+ // Calculate how much data we read into the cache, and how much data
+ // we read into the users buffer.
+ //
+
+ if ( IrpContext->Specific.Read.ReadOffset > irpSp->Parameters.Read.Length ) {
+
+ ASSERT(Icb->NpFcb->CacheBuffer != NULL ) ; // this had better be there
+
+ Icb->NpFcb->CacheDataSize =
+ IrpContext->Specific.Read.ReadOffset -
+ irpSp->Parameters.Read.Length;
+
+ irp->IoStatus.Information = irpSp->Parameters.Read.Length;
+
+ } else {
+
+ Icb->NpFcb->CacheDataSize = 0;
+ irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ DebugTrace( 0, Dbg, "BytesRead -> %08lx\n", irp->IoStatus.Information );
+ DebugTrace(-1, Dbg, "BurstRead -> %08lx\n", status );
+
+ Stats.PacketBurstReadNcps++;
+ return status;
+}
+
+VOID
+BuildBurstReadRequest(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG Handle,
+ IN ULONG FileOffset,
+ IN ULONG Length
+ )
+{
+ PNCP_BURST_READ_REQUEST BurstRead;
+ PNONPAGED_SCB pNpScb;
+ ULONG Temp;
+
+ BurstRead = (PNCP_BURST_READ_REQUEST)(IrpContext->req);
+ pNpScb = IrpContext->pNpScb;
+
+ BurstRead->BurstHeader.Command = PEP_COMMAND_BURST;
+ BurstRead->BurstHeader.Flags = BURST_FLAG_END_OF_BURST;
+ BurstRead->BurstHeader.StreamType = 0x02;
+ BurstRead->BurstHeader.SourceConnection = pNpScb->SourceConnectionId;
+ BurstRead->BurstHeader.DestinationConnection = pNpScb->DestinationConnectionId;
+
+ LongByteSwap( BurstRead->BurstHeader.SendDelayTime, pNpScb->NwReceiveDelay );
+
+ pNpScb->CurrentBurstDelay = pNpScb->NwReceiveDelay;
+
+ Temp = sizeof( NCP_BURST_READ_REQUEST ) - sizeof( NCP_BURST_HEADER );
+ LongByteSwap( BurstRead->BurstHeader.DataSize, Temp);
+
+ BurstRead->BurstHeader.BurstOffset = 0;
+
+ ShortByteSwap( BurstRead->BurstHeader.BurstLength, Temp );
+
+ BurstRead->BurstHeader.MissingFragmentCount = 0;
+
+ BurstRead->Function = 1;
+ BurstRead->Handle = Handle;
+
+ LongByteSwap(
+ BurstRead->TotalReadOffset,
+ IrpContext->Specific.Read.TotalReadOffset );
+
+ LongByteSwap(
+ BurstRead->TotalReadLength,
+ IrpContext->Specific.Read.TotalReadLength );
+
+ LongByteSwap( BurstRead->Offset, FileOffset );
+ LongByteSwap( BurstRead->Length, Length );
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_READ_REQUEST );
+}
+
+#ifdef NWDBG
+int DropReadPackets;
+#endif
+
+
+NTSTATUS
+BurstReadCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG DataOffset;
+ ULONG TotalBytesRead;
+ PUCHAR ReadData;
+ USHORT BytesThisPacket = 0;
+ UCHAR Flags;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "BurstReadCallback...\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %X\n", IrpContext );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server.
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_REMOTE_NOT_LISTENING;
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_REMOTE_NOT_LISTENING );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Stats.PacketBurstReadNcps++;
+
+ if ( Response != IrpContext->rsp ) {
+
+ //
+ // Acquire the SCB spin lock to protect access to the list
+ // of received data for this read.
+ //
+
+ KeAcquireSpinLock( &IrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ Status = ParseBurstReadResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ &Flags,
+ &DataOffset,
+ &BytesThisPacket,
+ &ReadData,
+ &TotalBytesRead );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ IrpContext->Specific.Read.Status = Status;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Update the list of data received, and copy the data to the users
+ // buffer.
+ //
+
+ RecordPacketReceipt( IrpContext, ReadData, DataOffset, BytesThisPacket, TRUE );
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+ Flags = IrpContext->Specific.Read.Flags;
+ }
+
+ //
+ // If this isn't the last packet setup for the next burst packet.
+ //
+
+ if ( !FlagOn( Flags, BURST_FLAG_END_OF_BURST ) ) {
+
+ DebugTrace(0, Dbg, "Waiting for another packet\n", 0);
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ DebugTrace(0, Dbg, "Received final packet\n", 0);
+
+ //
+ // Have we received all of the data? If not, VerifyBurstRead will
+ // send a missing data request.
+ //
+
+ if ( VerifyBurstRead( IrpContext ) ) {
+
+ //
+ // All the data for the current burst has been received, notify
+ // the thread that is sending the data.
+ //
+
+ if (NT_SUCCESS(IrpContext->Specific.Read.Status)) {
+
+ //
+ // If Irp allocation fails then it is possible for the
+ // packet to have been recorded but not copied into the
+ // user buffer. In this case leave the failure status.
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_SUCCESS;
+ }
+
+ NwSetIrpContextEvent( IrpContext );
+
+ }
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_SUCCESS );
+ return STATUS_SUCCESS;
+
+}
+
+VOID
+BurstReadTimeout(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a burst read timeout, i.e. no immediate response
+ to the current burst read request. It request to read the packet burst
+ data from the last valid received packet.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ DebugTrace(0, Dbg, "BurstReadTimeout\n", 0 );
+
+ //
+ // Re-request the data we haven't received.
+ //
+
+ if ( !IrpContext->Specific.Read.DataReceived ) {
+
+ DebugTrace( 0, Dbg, "No packets received, retranmit\n", 0 );
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // We never received any data. Try retransmitting the previous
+ // request.
+ //
+
+ PreparePacket( IrpContext, IrpContext->pOriginalIrp, IrpContext->TxMdl );
+ SendNow( IrpContext );
+
+ } else {
+
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ //
+ // Verify burst read will send a missing data request if one we
+ // have not received all of the data.
+ //
+
+ if ( VerifyBurstRead( IrpContext ) ) {
+ NwSetIrpContextEvent( IrpContext );
+ }
+ }
+
+ Stats.PacketBurstReadTimeouts++;
+}
+
+NTSTATUS
+ResubmitBurstRead (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a rerouted burst read. The burst request is
+ resubmitted on a new burst connection.
+
+Arguments:*
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Length, DataMdlBytes = 0 ;
+ PMDL DataMdl ;
+
+ DebugTrace( 0, Dbg, "ResubmitBurstRead\n", 0 );
+
+ //
+ // Recalculate the burst size, as MaxReceiveSize may have changed.
+ //
+
+ Length = MIN( IrpContext->pNpScb->MaxReceiveSize,
+ IrpContext->Specific.Read.RemainingLength );
+
+ //
+ // Make sure we dont ask for more than bytes described by MDL
+ //
+ DataMdl = IrpContext->Specific.Read.FullMdl;
+
+ while (DataMdl) {
+
+ DataMdlBytes += MmGetMdlByteCount( DataMdl );
+ DataMdl = DataMdl->Next;
+ }
+
+ Length = MIN( Length, DataMdlBytes ) ;
+
+ DebugTrace( 0, Dbg, "Requesting another burst, length = %ld\n", Length);
+
+ ASSERT( Length != 0 );
+
+ //
+ // Free the packet list, and reset all of the current burst context
+ // information.
+ //
+
+ FreePacketList( IrpContext );
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ SetConnectionTimeout( IrpContext->pNpScb, Length );
+
+ //
+ // Format and send the request.
+ //
+
+ BuildBurstReadRequest(
+ IrpContext,
+ *(ULONG UNALIGNED *)(&IrpContext->Icb->Handle[2]),
+ IrpContext->Specific.Read.FileOffset,
+ Length );
+
+ // Avoid SendNow setting the RetryCount back to the default
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ return Status;
+}
+
+VOID
+RecordPacketReceipt(
+ PIRP_CONTEXT IrpContext,
+ PVOID ReadData,
+ ULONG DataOffset,
+ USHORT ByteCount,
+ BOOLEAN CopyData
+ )
+/*++
+
+Routine Description:
+
+ This routine records the reciept of a burst read packet. It allocates
+ a burst read entry to record data start and length, and then inserts
+ the structure in order in the list of packets received for this burst.
+ It then copies the data to the user buffer.
+
+ BUGBUG - This routine could release the spin lock before doing the
+ data copy. Would this be useful?
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ReadData - A pointer to the data to copy.
+
+ DataOffset - The start offset of the data in the received packet.
+
+ ByteCount - The amount of data received.
+
+ CopyData - If FALSE, don't copy the data to the user's buffer. The
+ transport will do it.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PBURST_READ_ENTRY BurstReadEntry;
+ PBURST_READ_ENTRY ThisBurstReadEntry, NextBurstReadEntry;
+ PLIST_ENTRY ListEntry;
+#if NWDBG
+ BOOLEAN Insert = FALSE;
+#endif
+ USHORT ExtraBytes;
+
+ DebugTrace(0, Dbg, "RecordPacketReceipt\n", 0 );
+
+ IrpContext->Specific.Read.DataReceived = TRUE;
+
+ //
+ // Allocate and initialize a burst read entry.
+ //
+
+ BurstReadEntry = ALLOCATE_POOL( NonPagedPool, sizeof( BURST_READ_ENTRY ) );
+ if ( BurstReadEntry == NULL ) {
+ DebugTrace(0, Dbg, "Failed to allocate BurstReadEntry\n", 0 );
+ return;
+ }
+
+ //
+ // Insert this element in the ordered list of received packets.
+ //
+
+ if ( IsListEmpty( &IrpContext->Specific.Read.PacketList ) ) {
+
+#if NWDBG
+ Insert = TRUE;
+#endif
+
+ InsertHeadList(
+ &IrpContext->Specific.Read.PacketList,
+ &BurstReadEntry->ListEntry );
+
+ DebugTrace(0, Dbg, "First packet in the list\n", 0 );
+
+ } else {
+
+ //
+ // Walk the list of received packets, looking for the place to
+ // insert this entry. Walk the list backwards, since most of
+ // the time we will be appending to the list.
+ //
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Blink;
+ ThisBurstReadEntry = NULL;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ NextBurstReadEntry = ThisBurstReadEntry;
+ ThisBurstReadEntry = CONTAINING_RECORD(
+ ListEntry,
+ BURST_READ_ENTRY,
+ ListEntry );
+
+ if ( ThisBurstReadEntry->DataOffset <= DataOffset ) {
+
+ //
+ // Found the place in the list to insert this entry.
+ //
+
+ if ( ThisBurstReadEntry->DataOffset +
+ ThisBurstReadEntry->ByteCount > DataOffset ) {
+
+ //
+ // The start of this packet contains data which
+ // overlaps data we have received. Chuck the extra
+ // data.
+ //
+
+ ExtraBytes = (USHORT)( ThisBurstReadEntry->DataOffset +
+ ThisBurstReadEntry->ByteCount - DataOffset );
+
+ if ( ExtraBytes < ByteCount ) {
+ DataOffset += ExtraBytes;
+ (PCHAR)ReadData += ExtraBytes;
+ ByteCount -= ExtraBytes;
+ } else {
+ ByteCount = 0;
+ }
+
+ }
+
+ if ( NextBurstReadEntry != NULL &&
+ DataOffset + ByteCount > NextBurstReadEntry->DataOffset ) {
+
+ //
+ // This packet contains some new data, but some of it
+ // overlaps the NextBurstReadEntry. Simply ignore
+ // the overlap by adjusting the byte count.
+ //
+ // If the packet is all overlap, toss it.
+ //
+
+ ByteCount = (USHORT)( NextBurstReadEntry->DataOffset - DataOffset );
+ }
+
+ if ( ByteCount == 0 ) {
+ FREE_POOL( BurstReadEntry );
+ return;
+ }
+#if NWDBG
+ Insert = TRUE;
+#endif
+ InsertHeadList( ListEntry, &BurstReadEntry->ListEntry );
+ break;
+
+ } else {
+
+ ListEntry = ListEntry->Blink;
+ }
+ }
+
+ //
+ // Couldn't find the place to insert
+ //
+
+ ASSERT( Insert );
+ }
+
+ BurstReadEntry->DataOffset = DataOffset;
+ BurstReadEntry->ByteCount = ByteCount;
+
+ //
+ // Copy the data to our read buffer.
+ //
+
+ if ( CopyData ) {
+ CopyBufferToMdl(
+ IrpContext->Specific.Read.FullMdl,
+ DataOffset,
+ ReadData,
+ ByteCount );
+ }
+
+ return;
+}
+
+#include <packon.h>
+
+typedef struct _MISSING_DATA_ENTRY {
+ ULONG DataOffset;
+ USHORT ByteCount;
+} MISSING_DATA_ENTRY, *PMISSING_DATA_ENTRY;
+
+#include <packoff.h>
+
+BOOLEAN
+VerifyBurstRead(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies the set of response to a burst read request.
+ If some data is missing a missing packet request is sent.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ TRUE - All the data was received.
+
+ FALSE - Some data was missing.
+
+--*/
+{
+ ULONG CurrentOffset = 0;
+ PLIST_ENTRY ListEntry;
+ PBURST_READ_ENTRY BurstReadEntry;
+ USHORT MissingFragmentCount = 0;
+ USHORT ByteCount;
+ ULONG DataOffset;
+ MISSING_DATA_ENTRY UNALIGNED *MissingDataEntry;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "VerifyBurstRead\n", 0 );
+
+ //
+ // Acquire the SCB spin lock to protect access to the list
+ // of received data for this read.
+ //
+
+ KeAcquireSpinLock(&IrpContext->pNpScb->NpScbSpinLock, &OldIrql);
+
+#ifdef NWDBG
+ //
+ // Verify that the list is in order.
+ //
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Flink;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ BurstReadEntry = CONTAINING_RECORD( ListEntry, BURST_READ_ENTRY, ListEntry );
+ ASSERT ( BurstReadEntry->DataOffset >= CurrentOffset);
+ CurrentOffset = BurstReadEntry->DataOffset + BurstReadEntry->ByteCount;
+ ListEntry = ListEntry->Flink;
+ }
+
+ CurrentOffset = 0;
+
+#endif
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Flink;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ BurstReadEntry = CONTAINING_RECORD( ListEntry, BURST_READ_ENTRY, ListEntry );
+ if ( BurstReadEntry->DataOffset != CurrentOffset) {
+
+ //
+ // There is a hole in the data, fill in a missing packet entry.
+ //
+
+ MissingDataEntry = (MISSING_DATA_ENTRY UNALIGNED *)
+ &IrpContext->req[ sizeof( NCP_BURST_HEADER ) +
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY ) ];
+
+ DataOffset = CurrentOffset + SIZE_ADJUST( IrpContext );
+ LongByteSwap( MissingDataEntry->DataOffset, DataOffset );
+
+ ByteCount = (USHORT)( BurstReadEntry->DataOffset - CurrentOffset );
+ ShortByteSwap( MissingDataEntry->ByteCount, ByteCount );
+
+ ASSERT( BurstReadEntry->DataOffset - CurrentOffset <= IrpContext->pNpScb->MaxReceiveSize );
+
+ DebugTrace(0, Dbg, "Missing data at offset %ld\n", DataOffset );
+ DebugTrace(0, Dbg, "Missing %d bytes\n", ByteCount );
+ DebugTrace(0, Dbg, "CurrentOffset: %d\n", CurrentOffset );
+
+ MissingFragmentCount++;
+ }
+
+ CurrentOffset = BurstReadEntry->DataOffset + BurstReadEntry->ByteCount;
+ ListEntry = ListEntry->Flink;
+ }
+
+ //
+ // Any data missing off the end?
+ //
+
+ if ( CurrentOffset <
+ IrpContext->Specific.Read.BurstSize ) {
+
+ //
+ // There is a hole in the data, fill in a missing packet entry.
+ //
+
+ MissingDataEntry = (PMISSING_DATA_ENTRY)
+ &IrpContext->req[ sizeof( NCP_BURST_HEADER ) +
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY ) ];
+
+ DataOffset = CurrentOffset + SIZE_ADJUST( IrpContext );
+ LongByteSwap( MissingDataEntry->DataOffset, DataOffset );
+
+ ByteCount = (USHORT)( IrpContext->Specific.Read.BurstSize - CurrentOffset );
+ ShortByteSwap( MissingDataEntry->ByteCount, ByteCount );
+
+ ASSERT( IrpContext->Specific.Read.BurstSize - CurrentOffset < IrpContext->pNpScb->MaxReceiveSize );
+
+ DebugTrace(0, Dbg, "Missing data at offset %ld\n", MissingDataEntry->DataOffset );
+ DebugTrace(0, Dbg, "Missing %d bytes\n", MissingDataEntry->ByteCount );
+
+ MissingFragmentCount++;
+ }
+
+
+ if ( MissingFragmentCount == 0 ) {
+
+ //
+ // This read is now complete. Don't process any more packets until
+ // the next packet is sent.
+ //
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+
+ KeReleaseSpinLock(&IrpContext->pNpScb->NpScbSpinLock, OldIrql);
+
+ DebugTrace(-1, Dbg, "VerifyBurstRead -> TRUE\n", 0 );
+
+ return( TRUE );
+
+ } else {
+
+ KeReleaseSpinLock(&IrpContext->pNpScb->NpScbSpinLock, OldIrql);
+
+ //
+ // The server dropped a packet, adjust the timers.
+ //
+
+ NwProcessReceiveBurstFailure( IrpContext->pNpScb, MissingFragmentCount );
+
+ //
+ // Request the missing data.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_PACKET );
+
+ //
+ // Update burst request offset since we are about to request
+ // more data. Note that this will reset the retry count,
+ // thus giving the server full timeout time to return the
+ // missing data.
+ //
+
+ BuildRequestPacket(
+ IrpContext,
+ BurstReadCallback,
+ "Bws",
+ 0, // Frame size for this request is 0
+ 0, // Offset of data
+ BURST_FLAG_SYSTEM_PACKET,
+ MissingFragmentCount,
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY )
+ );
+
+ PrepareAndSendPacket( IrpContext );
+
+ Stats.PacketBurstReadTimeouts++;
+
+ DebugTrace(-1, Dbg, "VerifyBurstRead -> FALSE\n", 0 );
+ return( FALSE );
+ }
+}
+
+
+VOID
+FreePacketList(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees the received packet list for a burst read.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY ListHead;
+ PBURST_READ_ENTRY BurstReadEntry;
+
+ ListHead = &IrpContext->Specific.Read.PacketList;
+ while ( !IsListEmpty( ListHead ) ) {
+ BurstReadEntry = CONTAINING_RECORD( ListHead->Flink, BURST_READ_ENTRY, ListEntry );
+ RemoveHeadList( ListHead );
+ FREE_POOL( BurstReadEntry );
+ }
+}
+
+NTSTATUS
+BurstReadReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ PMDL *pReceiveMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine builds an MDL to receive burst read data. This routine
+ is called at data indication time.
+
+ This routine is called with the non paged SCB spin lock held.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BytesAvailable - The number of bytes in the entire packet.
+
+ BytesAccepted - Returns the number of bytes accepted from the packet.
+
+ Response - A pointer to the indication buffer.
+
+Return Value:
+
+ Mdl - An MDL to receive the data.
+ This routine raise an exception if it cannot receive the data.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG DataOffset;
+ ULONG TotalBytesRead;
+ PUCHAR ReadData;
+ USHORT BytesThisPacket;
+ UCHAR Flags;
+ PMDL PartialMdl;
+
+ DebugTrace(0, Dbg, "Burst read receive\n", 0);
+
+ Status = ParseBurstReadResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ &Flags,
+ &DataOffset,
+ &BytesThisPacket,
+ &ReadData,
+ &TotalBytesRead );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace(0, Dbg, "Failed to parse burst read response\n", 0);
+ return Status;
+ }
+
+ //
+ // We can accept up to the size of a burst read header, plus
+ // 3 bytes of fluff for the unaligned read case.
+ //
+
+ *BytesAccepted = ReadData - Response;
+ ASSERT( *BytesAccepted <= sizeof(NCP_BURST_READ_RESPONSE) + 3 );
+
+ RecordPacketReceipt( IrpContext, ReadData, DataOffset, BytesThisPacket, FALSE );
+
+ IrpContext->Specific.Read.Flags = Flags;
+
+ //
+ // If we did a read at EOF the netware server will return 0 bytes read,
+ // no error.
+ //
+
+ ASSERT( IrpContext->Specific.Read.FullMdl != NULL );
+
+ if ( BytesThisPacket > 0 ) {
+
+ PartialMdl = AllocateReceivePartialMdl(
+ IrpContext->Specific.Read.FullMdl,
+ DataOffset,
+ BytesThisPacket );
+
+ if ( !PartialMdl ) {
+ IrpContext->Specific.Read.Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // Record Mdl to free when CopyIndicatedData or Irp completed.
+ IrpContext->Specific.Read.PartialMdl = PartialMdl;
+
+ } else {
+
+ PartialMdl = NULL;
+
+ }
+
+ *pReceiveMdl = PartialMdl;
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+ParseBurstReadResponse(
+ IN PIRP_CONTEXT IrpContext,
+ PUCHAR Response,
+ ULONG BytesAvailable,
+ PUCHAR Flags,
+ PULONG DataOffset,
+ PUSHORT BytesThisPacket,
+ PUCHAR *ReadData,
+ PULONG TotalBytesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine parses a burst read response.
+
+ This routine must be called the the nonpagd SCB spinlock held.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Response - A pointer to the response buffer.
+
+ BytesAvailable - The number of bytes in the packet.
+
+ Flags - Returns the Burst Flags
+
+ DataOffset - Returns the data offset (within the burst) of the
+ data in this packet.
+
+ BytesThisPacket - Returns the number of file data bytes in this packet.
+
+ ReadData - Returns a pointer to the start of the file data in the
+ packet buffer.
+
+ TotalBytesRead - Returns the number of byte of file data in the
+ entire burst.
+
+
+Return Value:
+
+ The status of the read.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Result;
+ PNCP_BURST_READ_RESPONSE ReadResponse;
+
+ DebugTrace(+1, Dbg, "ParseBurstReadResponse\n", 0);
+
+ ReadResponse = (PNCP_BURST_READ_RESPONSE)Response;
+ *Flags = ReadResponse->BurstHeader.Flags;
+
+#ifdef NWDBG
+ //
+ // Bad net simulator.
+ //
+
+ if ( DropReadPackets != 0 ) {
+ if ( ( rand() % DropReadPackets ) == 0 ) {
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ DebugTrace( 0, Dbg, "Dropping packet\n", 0 );
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_UNSUCCESSFUL );
+ return ( STATUS_UNSUCCESSFUL );
+ }
+ }
+
+#endif
+
+ //
+ // If this isn't the last packet, setup for the next burst packet.
+ //
+
+ if ( !FlagOn( *Flags, BURST_FLAG_END_OF_BURST ) ) {
+
+ DebugTrace(0, Dbg, "Waiting for another packet\n", 0);
+
+ //
+ // Once we receive the first packet in a read response be aggresive
+ // about timing out while waiting for the rest of the burst.
+ //
+
+ IrpContext->pNpScb->TimeOut = IrpContext->pNpScb->SendTimeout ;
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+
+ LongByteSwap( *DataOffset, ReadResponse->BurstHeader.BurstOffset );
+ ShortByteSwap( *BytesThisPacket, ReadResponse->BurstHeader.BurstLength );
+
+ //
+ // How much data was received?
+ //
+
+ if ( IsListEmpty( &IrpContext->Specific.Read.PacketList ) ) {
+
+ DebugTrace(0, Dbg, "Expecting initial response\n", 0);
+
+ //
+ // This is the initial burst response packet.
+ //
+
+ if ( *DataOffset != 0 ) {
+
+ DebugTrace(0, Dbg, "Invalid initial response tossed\n", 0);
+
+ //
+ // This is actually a subsequent response. Toss it.
+ // BUGBUG - Can we handle it?
+ //
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_UNSUCCESSFUL );
+ IrpContext->pNpScb->OkToReceive = TRUE;
+
+ return ( STATUS_UNSUCCESSFUL );
+ }
+
+ Result = ReadResponse->Result;
+ LongByteSwap( *TotalBytesRead, ReadResponse->BytesRead );
+
+ Status = NwBurstResultToNtStatus( Result );
+ IrpContext->Specific.Read.Status = Status;
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Update the burst request number now.
+ //
+
+ DebugTrace(0, Dbg, "Read completed, error = %X\n", Status );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST );
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", Status );
+ return( Status );
+ }
+
+ if ( Result == 3 || *BytesThisPacket < 8 ) { // No data
+ *TotalBytesRead = 0;
+ *BytesThisPacket = 8;
+ }
+
+ *ReadData = Response + sizeof(NCP_BURST_READ_RESPONSE);
+
+ IrpContext->Specific.Read.BurstSize = *TotalBytesRead;
+
+ //
+ // Bytes this packet includes a LONG status and a LONG byte total.
+ // Adjust the count to reflect the number of data bytes actually
+ // shipped.
+ //
+
+ *BytesThisPacket -= sizeof( ULONG ) + sizeof( ULONG );
+
+ //
+ // Adjust this data if the read was not DWORD aligned.
+ //
+
+ if ( (IrpContext->Specific.Read.FileOffset & 0x03) != 0
+ && *BytesThisPacket != 0 ) {
+
+ *ReadData += IrpContext->Specific.Read.FileOffset & 0x03;
+ *BytesThisPacket -= (USHORT)IrpContext->Specific.Read.FileOffset & 0x03;
+ }
+
+ DebugTrace(0, Dbg, "Initial response\n", 0);
+ DebugTrace(0, Dbg, "Result = %ld\n", Result);
+ DebugTrace(0, Dbg, "Total bytes read = %ld\n", *TotalBytesRead );
+
+ } else {
+
+ //
+ // Intermediate response packet.
+ //
+
+ *ReadData = Response + sizeof( NCP_BURST_HEADER );
+ *DataOffset -= SIZE_ADJUST( IrpContext );
+
+ }
+
+ DebugTrace(0, Dbg, "DataOffset = %ld\n", *DataOffset );
+ DebugTrace(0, Dbg, "# bytes received = %d\n", *BytesThisPacket );
+
+ if ( *DataOffset > IrpContext->Specific.Read.BurstSize ||
+ *DataOffset + *BytesThisPacket > IrpContext->Specific.Read.BurstSize ) {
+
+ DebugTrace(0, Dbg, "Invalid response tossed\n", 0);
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_SUCCESS );
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ return ( STATUS_UNSUCCESSFUL );
+ }
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+}
+
+
+PMDL
+AllocateReceivePartialMdl(
+ PMDL FullMdl,
+ ULONG DataOffset,
+ ULONG BytesThisPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a partial MDL to receive read data. This
+ routine is called at receive indication time.
+
+Arguments:
+
+ FullMdl - The FullMdl for the buffer.
+
+ DataOffset - The offset into the buffer where the data is to be received.
+
+ BytesThisPacket - The number of data bytes to be received into the buffer.
+
+Return Value:
+
+ MDL - A pointer to an MDL to receive the data
+ This routine raises an exception if it cannot allocate an MDL.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PUCHAR BufferStart, BufferEnd;
+ PMDL InitialMdl, NextMdl;
+ PMDL ReceiveMdl, PreviousReceiveMdl;
+ ULONG BytesThisMdl;
+
+ BufferStart = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) + DataOffset;
+ BufferEnd = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) +
+ MmGetMdlByteCount( FullMdl );
+
+ //
+ // Walk the MDL chain look for the MDL for the actual buffer for the
+ // start of this data.
+ //
+
+ while ( BufferStart >= BufferEnd ) {
+ DataOffset -= MmGetMdlByteCount( FullMdl );
+ FullMdl = FullMdl->Next;
+
+ //
+ // if more data than expected, dont dereference NULL! see next loop.
+ //
+ if (!FullMdl) {
+ ASSERT(FALSE) ;
+ break ;
+ }
+
+ BufferStart = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) + DataOffset;
+ BufferEnd = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) +
+ MmGetMdlByteCount( FullMdl );
+ }
+
+ PreviousReceiveMdl = NULL;
+ InitialMdl = NULL;
+ BytesThisMdl = (ULONG)(BufferEnd - BufferStart);
+
+ //
+ // Check FullMdl to cover the case where the server returns more data
+ // than requested.
+ //
+
+ while (( BytesThisPacket != 0 ) &&
+ ( FullMdl != NULL )) {
+
+ BytesThisMdl = MIN( BytesThisMdl, BytesThisPacket );
+
+ //
+ // Some of the data fits in the first part of the MDL;
+ //
+
+ ReceiveMdl = ALLOCATE_MDL(
+ BufferStart,
+ BytesThisMdl,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( ReceiveMdl == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ if ( InitialMdl == NULL ) {
+ InitialMdl = ReceiveMdl;
+ }
+
+ IoBuildPartialMdl(
+ FullMdl,
+ ReceiveMdl,
+ BufferStart,
+ BytesThisMdl );
+
+ if ( PreviousReceiveMdl != NULL ) {
+ PreviousReceiveMdl->Next = ReceiveMdl;
+ }
+
+ PreviousReceiveMdl = ReceiveMdl;
+
+ BytesThisPacket -= BytesThisMdl;
+
+ FullMdl = FullMdl->Next;
+
+ if ( FullMdl != NULL) {
+ BytesThisMdl = MmGetMdlByteCount( FullMdl );
+ BufferStart = MmGetMdlVirtualAddress( FullMdl );
+ }
+
+ }
+
+ if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
+
+ //
+ // Cleanup allocated MDLs
+ //
+
+ while ( InitialMdl != NULL ) {
+ NextMdl = InitialMdl->Next;
+ FREE_MDL( InitialMdl );
+ InitialMdl = NextMdl;
+ }
+
+ DebugTrace( 0, Dbg, "AllocateReceivePartialMdl Failed\n", 0 );
+ }
+
+ DebugTrace( 0, Dbg, "AllocateReceivePartialMdl -> %08lX\n", InitialMdl );
+ return( InitialMdl );
+}
+
+
+VOID
+SetConnectionTimeout(
+ PNONPAGED_SCB pNpScb,
+ ULONG Length
+ )
+/*++
+
+Routine Description:
+
+
+ The server will pause NwReceiveDelay between packets. Make sure we have our timeout
+ so that we will take that into account.
+
+Arguments:
+
+ pNpScb - Connection
+
+ Length - Length of the burst in bytes
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ ULONG TimeInNwUnits;
+ LONG SingleTimeInNwUnits;
+
+ SingleTimeInNwUnits = pNpScb->NwSingleBurstPacketTime + pNpScb->NwReceiveDelay;
+
+ TimeInNwUnits = SingleTimeInNwUnits * ((Length / pNpScb->MaxPacketSize) + 1) +
+ pNpScb->NwLoopTime;
+
+ //
+ // Convert to 1/18ths of a second ticks and multiply by a fudge
+ // factor. The fudge factor is expressed as a percentage. 100 will
+ // mean no fudge.
+ //
+
+ pNpScb->MaxTimeOut = (SHORT)( ((TimeInNwUnits / 555) *
+ (ULONG)ReadTimeoutMultiplier) / 100 + 1);
+
+ //
+ // Now make sure we have a meaningful lower and upper limit.
+ //
+ if (pNpScb->MaxTimeOut < 2)
+ {
+ pNpScb->MaxTimeOut = 2 ;
+ }
+
+ if (pNpScb->MaxTimeOut > (SHORT)MaxReadTimeout)
+ {
+ pNpScb->MaxTimeOut = (SHORT)MaxReadTimeout ;
+ }
+
+ pNpScb->TimeOut = pNpScb->SendTimeout = pNpScb->MaxTimeOut;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->MaxTimeout = %08lx\n", pNpScb->MaxTimeOut );
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastRead (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine does a fast cached read bypassing the usual file system
+ entry routine (i.e., without the Irp). It is used to do a copy read
+ of a cached file object. For a complete description of the arguments
+ see CcCopyRead.
+
+Arguments:
+
+ FileObject - Pointer to the file object being read.
+
+ FileOffset - Byte offset in file for desired data.
+
+ Length - Length of desired data in bytes.
+
+ Wait - FALSE if caller may not block, TRUE otherwise
+
+ Buffer - Pointer to output buffer to which data should be copied.
+
+ IoStatus - Pointer to standard I/O status block to receive the status
+ for the transfer.
+
+Return Value:
+
+ FALSE - if Wait was supplied as FALSE and the data was not delivered, or
+ if there is an I/O error.
+
+ TRUE - if the data is being delivered
+
+--*/
+
+{
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+ ULONG bytesRead;
+ ULONG offset;
+
+ DebugTrace(+1, Dbg, "NwFastRead...\n", 0);
+
+ //
+ // Special case a read of zero length
+ //
+
+ if (Length == 0) {
+
+ //
+ // A zero length transfer was requested.
+ //
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = 0;
+
+ DebugTrace(+1, Dbg, "NwFastRead -> TRUE\n", 0);
+ return TRUE;
+ }
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not FCB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ DebugTrace(-1, Dbg, "NwFastRead -> FALSE\n", 0);
+ return FALSE;
+ }
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+ offset = FileOffset->LowPart;
+
+ bytesRead = CacheRead(
+ fcb->NonPagedFcb,
+ offset,
+ Length,
+ Buffer,
+ TRUE );
+
+ if ( bytesRead != 0 ) {
+
+ ASSERT( bytesRead == Length );
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = bytesRead;
+#ifndef NT1057
+ FileObject->CurrentByteOffset.QuadPart += Length;
+#endif
+ DebugTrace(-1, Dbg, "NwFastRead -> TRUE\n", 0);
+ return( TRUE );
+
+ } else {
+
+ DebugTrace(-1, Dbg, "NwFastRead -> FALSE\n", 0);
+ return( FALSE );
+
+ }
+}
+#endif
diff --git a/private/nw/rdr/scavengr.c b/private/nw/rdr/scavengr.c
new file mode 100644
index 000000000..ac69a9ab8
--- /dev/null
+++ b/private/nw/rdr/scavengr.c
@@ -0,0 +1,689 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Scavengr.c
+
+Abstract:
+
+ This module implements the Netware Redirector scavenger thread.
+
+Author:
+
+ Manny Weiser [MannyW] 15-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_SCAVENGER)
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+#ifdef NWDBG
+DWORD DumpIcbFlag = 0 ;
+#endif
+
+VOID
+CleanupVcbs(
+ LARGE_INTEGER Now
+ );
+
+#ifdef ALLOC_PRAGMA
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwAllocateExtraIrpContext )
+#pragma alloc_text( PAGE1, NwFreeExtraIrpContext )
+#pragma alloc_text( PAGE1, CleanupVcbs )
+#pragma alloc_text( PAGE1, CleanupScbs )
+#pragma alloc_text( PAGE1, DisconnectTimedOutScbs )
+#endif
+
+#endif
+
+//
+// Not pageable:
+//
+// NwScavengerRoutine - Acquires a spin lock.
+//
+
+VOID
+NwScavengerRoutine(
+ IN PWORK_QUEUE_ITEM WorkItem
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the scavenger. The scavenger runs
+ periodically in the context of an executive worker thread to
+ do background cleanup operations on redirector data.
+
+Arguments:
+
+ WorkItem - The work item for this routine.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LARGE_INTEGER Now;
+ PMDL LineChangeMdl;
+ PWORK_QUEUE_ITEM LineChangeWorkItem;
+ KIRQL OldIrql;
+
+ PAGED_CODE();
+
+
+ DebugTrace(+1, Dbg, "NwScavengerRoutine\n", 0);
+
+ KeQuerySystemTime( &Now );
+
+#ifdef NWDBG
+ if (DumpIcbFlag != 0)
+ DumpIcbs();
+#endif
+
+ //
+ // Try to free unused VCBs.
+ //
+
+ CleanupVcbs(Now);
+
+ //
+ // Try disconnect from SCBs that are timed out.
+ //
+
+ DisconnectTimedOutScbs(Now) ;
+
+ //
+ // Try to free unused SCBs.
+ //
+
+ CleanupScbs(Now);
+
+ //
+ // Flag we're finished now to avoid deadlock in stop timer.
+ //
+
+ KeAcquireSpinLock( &NwScavengerSpinLock, &OldIrql );
+
+ if ( DelayedProcessLineChange ) {
+
+ DebugTrace( 0, Dbg, "Scavenger processing a delayed line change notification.\n", 0 );
+
+ LineChangeMdl = DelayedLineChangeIrp->MdlAddress;
+ LineChangeWorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
+
+ if ( LineChangeWorkItem == NULL ) {
+
+ //
+ // If we couldn't get a work queue item, just blow
+ // it all off for now.
+ //
+
+ FREE_POOL( LineChangeMdl->MappedSystemVa );
+ FREE_MDL( LineChangeMdl );
+ FREE_IRP( DelayedLineChangeIrp );
+
+ DelayedLineChangeIrp = NULL;
+ DelayedProcessLineChange = FALSE;
+ WorkerRunning = FALSE;
+
+ KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
+
+ } else {
+
+ //
+ // Leave WorkRunning set to TRUE so that the scavenger can't run
+ // while the process line change is running, but clear the line
+ // change flag. The FspProcessLineChange function will clear the
+ // WorkerRunning flag.
+ //
+
+ DelayedProcessLineChange = FALSE;
+ KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
+
+ //
+ // Use the user buffer field as a convenient place to remember where
+ // the address of the WorkQueueItem. We can get away with this since
+ // we don't let this IRP complete.
+ //
+
+ DelayedLineChangeIrp->UserBuffer = LineChangeWorkItem;
+
+ //
+ // Process the line change in the FSP.
+ //
+
+ ExInitializeWorkItem( LineChangeWorkItem, FspProcessLineChange, DelayedLineChangeIrp );
+ ExQueueWorkItem( LineChangeWorkItem, DelayedWorkQueue );
+
+ }
+
+ } else {
+
+ //
+ // No line change happened while the scavenger was running.
+ //
+
+ WorkerRunning = FALSE;
+ KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
+
+ }
+
+ //
+ // Unlock discardable code, if we are inactive. Don't block
+ // if can't get resource.
+ //
+
+ NwUnlockCodeSections(FALSE);
+
+
+ DebugTrace(-1, Dbg, "NwScavengerRoutine\n", 0);
+ return;
+}
+
+
+VOID
+CleanupScbs(
+ LARGE_INTEGER Now
+ )
+/*++
+
+Routine Description:
+
+ This routine tries to free unused VCB structures.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PLIST_ENTRY NextScbQueueEntry;
+ PSCB pScb;
+ LIST_ENTRY DyingScbs;
+ LARGE_INTEGER KillTime ;
+
+ DebugTrace(+1, Dbg, "CleanupScbs\n", 0);
+
+ //
+ // Calculate KillTime = Now - 2 minutes.
+ //
+
+ InitializeListHead( &DyingScbs );
+
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
+
+ //
+ // Scan through the SCBs holding the RCB.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ //
+ // find all SCBs that are no longer usable and put them on the dying list.
+ // we will take a second pass thru to remove timed out ones, based on
+ // what is left.
+ //
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry )
+ {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if ( ( pNpScb->Reference == 0 ) &&
+ ( ( pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart ) ||
+ ( pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) ) )
+ {
+ DebugTrace( 0, Dbg,
+ "Moving SCB %08lx to dead list\n", pNpScb);
+
+ //
+ // The SCB has no references and is not logged in nor attached.
+ //
+
+ RemoveEntryList( &pNpScb->ScbLinks );
+ InsertHeadList( &DyingScbs, &pNpScb->ScbLinks );
+ }
+
+#ifdef MSWDBG
+ //
+ // Look for blocked connections. If there's something
+ // queued for this server yet nothing was added or removed
+ // since the last time the scavenger ran then stop
+ //
+
+ if ((!IsListEmpty( &pNpScb->Requests ) ) &&
+ (pNpScb->RequestQueued == FALSE) &&
+ (pNpScb->RequestDequeued == FALSE )) {
+
+ DebugTrace( 0, Dbg, "Server %08lx seems to be locked up!\n", pNpScb );
+ ASSERT( FALSE );
+
+ } else {
+
+ pNpScb->RequestQueued = FALSE;
+ pNpScb->RequestDequeued = FALSE;
+
+ }
+#endif
+ }
+
+ //
+ // Now that the dying SCBs are off the ScbQueue we can release
+ // the SCB spin lock.
+ //
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Walk the list of Dying SCBs and kill them off. Note that we are
+ // still holding the RCB.
+ //
+
+ while ( !IsListEmpty( &DyingScbs ) ) {
+
+ pNpScb = CONTAINING_RECORD( DyingScbs.Flink, NONPAGED_SCB, ScbLinks );
+ pScb = pNpScb->pScb;
+
+ RemoveHeadList( &DyingScbs );
+ NwDeleteScb( pScb );
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ DebugTrace(-1, Dbg, "CleanupScbs\n", 0);
+
+}
+
+VOID
+CleanupVcbs(
+ LARGE_INTEGER Now
+ )
+/*++
+
+Routine Description:
+
+ This routine tries to free unused VCB structures.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY VcbQueueEntry;
+ PLIST_ENTRY NextVcbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ PVCB pVcb;
+ LARGE_INTEGER KillTime;
+
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN VcbDeleted;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CleanupVcbs...\n", 0 );
+
+ //
+ // Calculate KillTime = Now - 5 minutes.
+ //
+
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_VCB_KEEP_TIME );
+
+ //
+ // Scan through the SCBs.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ //
+ // Reference the SCB so that it won't go away when we release
+ // the SCB spin lock.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ pScb = pNpScb->pScb;
+
+ if ( pScb == NULL) {
+
+ //
+ // This must be the permanent SCB. Just skip it.
+ //
+
+ ASSERT( pNpScb == &NwPermanentNpScb );
+
+ } else {
+
+ //
+ // Get an irp context and get to the head of the queue.
+ //
+
+ if ( NwAllocateExtraIrpContext( &IrpContext, pNpScb ) ) {
+
+ IrpContext->pNpScb = pNpScb;
+ IrpContext->pScb = pNpScb->pScb;
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ VcbDeleted = TRUE;
+
+ //
+ // NwCleanupVcb releases the RCB, but we can't be guaranteed
+ // the state of the Vcb list when we release the RCB.
+ //
+ // If we need to cleanup a VCB, release the lock, and start
+ // processing the list again.
+ //
+
+ while ( VcbDeleted ) {
+
+ VcbDeleted = FALSE;
+
+ for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink ;
+ VcbQueueEntry != &pScb->ScbSpecificVcbQueue;
+ VcbQueueEntry = NextVcbQueueEntry ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+ NextVcbQueueEntry = VcbQueueEntry->Flink;
+
+ //
+ // The VCB has no references, and hasn't been used for
+ // a long time. Kill it.
+ //
+
+ if ( pVcb->Reference == 0 ) {
+
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(0, Dbg, "Cleaning up VCB %08lx\n", pVcb );
+ DebugTrace(0, Dbg, "VCB name = %wZ\n", &pVcb->Name );
+
+ // Lock down so that we can send a packet.
+ NwReferenceUnlockableCodeSection();
+
+ NwCleanupVcb( pVcb, IrpContext );
+
+ NwDereferenceUnlockableCodeSection ();
+
+ //
+ // Get back to the head of the queue, re-acquire
+ // the VCB, and restart the processing of this list.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ VcbDeleted = TRUE;
+
+ break;
+ }
+
+ } // for
+
+ } // while
+
+ } else {
+
+ IrpContext = NULL;
+ DebugTrace( 0, Dbg, "Couldn't cleanup SCB: %08lx\n", pNpScb );
+
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ }
+
+ //
+ // Free the irp context allocated for this SCB.
+ //
+
+ if ( IrpContext != NULL ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwFreeExtraIrpContext( IrpContext );
+ IrpContext = NULL;
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DebugTrace(-1, Dbg, "CleanupVcbs -> VOID\n", 0 );
+}
+
+
+VOID
+DisconnectTimedOutScbs(
+ LARGE_INTEGER Now
+ )
+/*++
+
+Routine Description:
+
+ This routine disconnects any timed out SCBs before they get
+ nuked by CleanupScbs() which does not disconnect.
+
+ NOTE: The SCB's are destroyed on a timeout for a couple of
+ reasons. The first is because if we used a reference count then
+ normal use of UNCs would cause us to be continually reconnecting.
+ Another is in FindNearestServer where its useful to collect the
+ Near servers that are out of connections so we can avoid them when
+ we iterate through the 5 nearest servers and we escalate to General
+ SAP response.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ LARGE_INTEGER KillTime ;
+
+ PIRP_CONTEXT IrpContext = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "DisconnectTimedOutScbs...\n", 0 );
+
+ //
+ // Calculate KillTime = Now - 5 minutes.
+ //
+
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
+
+ //
+ // Scan through the SCBs.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue )
+ {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+
+ if ( (pNpScb != &NwPermanentNpScb) &&
+ (pNpScb->Reference == 0 ) &&
+ (pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart) )
+ {
+ //
+ // Reference the SCB so that it won't go away when we release
+ // the SCB spin lock.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Not the permanent SCB and the reference count is the one
+ // we just added, So this is really at zero & has not been used
+ // for a while. Note we only allocate the IrpContext once.
+ //
+ if ( IrpContext ||
+ NwAllocateExtraIrpContext( &IrpContext, pNpScb ) )
+ {
+
+ IrpContext->pNpScb = pNpScb;
+
+ // Lock down so that we can send a packet.
+ NwReferenceUnlockableCodeSection();
+
+ //
+ // get to front of queue and recheck to make sure we are
+ // still with a ref count of 1.
+ //
+ NwAppendToQueueAndWait( IrpContext );
+
+ if (pNpScb->Reference == 1)
+ {
+ //
+ // make sure we do not reconnect.
+ //
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // This will result in a logoff and/or disconnect as
+ // need.
+ //
+ NwLogoffAndDisconnect(IrpContext, pNpScb) ;
+ }
+
+ NwDequeueIrpContext(IrpContext, FALSE) ;
+
+ NwDereferenceUnlockableCodeSection ();
+
+
+ }
+ else
+ {
+ //
+ // Could not allocate IrpContext. Oh well, we'll just leave
+ // this connection for the watch dog.
+ //
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NwDereferenceScb( pNpScb );
+ }
+ else
+ {
+ //
+ // not timed out or is permanent SCB. dont disconnect.
+ //
+ }
+
+ ScbQueueEntry = pNpScb->ScbLinks.Flink;
+ }
+
+ if ( IrpContext )
+ NwFreeExtraIrpContext( IrpContext );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DebugTrace(-1, Dbg, "DisconnectTimedOutScbs -> VOID\n", 0 );
+}
+
+BOOLEAN
+NwAllocateExtraIrpContext(
+ OUT PIRP_CONTEXT *ppIrpContext,
+ IN PNONPAGED_SCB pNpScb
+ )
+{
+ PIRP Irp;
+ BOOLEAN Success = TRUE;
+
+ try {
+
+ //
+ // Try to allocate an IRP
+ //
+
+ Irp = ALLOCATE_IRP( pNpScb->Server.pDeviceObject->StackSize, FALSE );
+ if ( Irp == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Try to allocate an IRP Context. This will
+ // raise an excpetion if it fails.
+ //
+
+ *ppIrpContext = AllocateIrpContext( Irp );
+ Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+ Success = FALSE;
+ }
+
+ return( Success );
+}
+
+VOID
+NwFreeExtraIrpContext(
+ IN PIRP_CONTEXT pIrpContext
+ )
+{
+ FREE_IRP( pIrpContext->pOriginalIrp );
+
+ pIrpContext->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
+
+ FreeIrpContext( pIrpContext );
+
+ return;
+}
+
diff --git a/private/nw/rdr/security.c b/private/nw/rdr/security.c
new file mode 100644
index 000000000..e1b2fbfba
--- /dev/null
+++ b/private/nw/rdr/security.c
@@ -0,0 +1,1009 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Security.c
+
+Abstract:
+
+ This module implements security related tasks in the
+ NetWare redirector.
+
+Author:
+
+ Colin Watson [ColinW] 05-Nov-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdio.h>
+
+PLOGON
+FindUserByName(
+ IN PUNICODE_STRING UserName
+ );
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_SECURITY)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, CreateAnsiUid )
+#pragma alloc_text( PAGE, MakeUidServer )
+#pragma alloc_text( PAGE, FindUser )
+#pragma alloc_text( PAGE, FindUserByName )
+#pragma alloc_text( PAGE, GetUid )
+#pragma alloc_text( PAGE, FreeLogon )
+#pragma alloc_text( PAGE, Logon )
+#pragma alloc_text( PAGE, Logoff )
+#endif
+
+
+VOID
+CreateAnsiUid(
+ OUT PCHAR aUid,
+ IN PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine converts the Uid into an array of ansi characters,
+ preserving the uniqueness and allocating the buffer in the process.
+
+ Note: aUid needs to be 17 bytes long.
+
+Arguments:
+
+ OUT PCHAR aUid,
+ IN PLARGE_INTEGER Uid
+
+Return Value:
+
+ Status
+
+--*/
+{
+ PAGED_CODE();
+
+ if (Uid->HighPart != 0) {
+ sprintf( aUid, "%lx%08lx\\", Uid->HighPart, Uid->LowPart );
+ } else {
+ sprintf( aUid, "%lx\\", Uid->LowPart );
+ }
+ return;
+}
+
+
+NTSTATUS
+MakeUidServer(
+ PUNICODE_STRING UidServer,
+ PLARGE_INTEGER Uid,
+ PUNICODE_STRING Server
+ )
+
+/*++
+
+Routine Description:
+
+ This routine makes a Unicode string of the form 3e7\servername
+
+Arguments:
+
+ OUT PUNICODE_STRING UidServer,
+ IN PLARGE_INTEGER Uid,
+ IN PUNICODE_STRING Server
+
+Return Value:
+
+ Status
+
+--*/
+{
+ //
+ // Translate the servername into the form 3e7\Server where 3e7
+ // is the value of the Uid.
+ //
+ UCHAR aUid[17];
+ ANSI_STRING AnsiString;
+ ULONG UnicodeLength;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ CreateAnsiUid( aUid, Uid);
+
+ RtlInitAnsiString( &AnsiString, aUid );
+
+ UnicodeLength = RtlAnsiStringToUnicodeSize(&AnsiString);
+
+ UidServer->MaximumLength = (USHORT)UnicodeLength + Server->Length;
+ UidServer->Buffer = ALLOCATE_POOL(PagedPool,UidServer->MaximumLength);
+
+ if (UidServer->Buffer == NULL) {
+ DebugTrace(-1, Dbg, "MakeUidServer -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlAnsiStringToUnicodeString( UidServer, &AnsiString, FALSE);
+ ASSERT(NT_SUCCESS(Status) && "MakeUidServer failed!");
+
+ Status = RtlAppendStringToString( (PSTRING)UidServer, (PSTRING)Server);
+ ASSERT(NT_SUCCESS(Status) && "MakeUidServer part 2 failed!");
+ return STATUS_SUCCESS;
+}
+
+
+PLOGON
+FindUser(
+ IN PLARGE_INTEGER Uid,
+ IN BOOLEAN ExactMatch
+ )
+
+/*++
+
+Routine Description:
+
+ This routine searches the LogonList for the user entry corresponding
+ to Uid.
+
+ Note: Rcb must be held to prevent LogonList being changed.
+
+Arguments:
+
+ IN PLARGE_INTEGER Uid
+
+ IN BOOLEAN ExactMatch - if TRUE, don't return a default
+
+Return Value:
+
+ None
+
+--*/
+{
+ PLIST_ENTRY LogonQueueEntry = LogonList.Flink;
+ PLOGON DefaultLogon = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "FindUser...\n", 0);
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", Uid->HighPart);
+ DebugTrace( 0, Dbg, " ->UserUidLow = %08lx\n", Uid->LowPart);
+ while ( LogonQueueEntry != &LogonList ) {
+
+ PLOGON Logon = CONTAINING_RECORD( LogonQueueEntry, LOGON, Next );
+
+ if ( (*Uid).QuadPart == Logon->UserUid.QuadPart ) {
+ DebugTrace(-1, Dbg, " ... %x\n", Logon );
+ return Logon;
+ }
+
+ LogonQueueEntry = Logon->Next.Flink;
+ }
+
+ if (ExactMatch) {
+ DebugTrace(-1, Dbg, " ... DefaultLogon NULL\n", 0 );
+ return NULL;
+ }
+
+ LogonQueueEntry = LogonList.Flink;
+ while ( LogonQueueEntry != &LogonList ) {
+
+ PLOGON Logon = CONTAINING_RECORD( LogonQueueEntry, LOGON, Next );
+
+ if (Logon->UserUid.QuadPart == DefaultLuid.QuadPart) {
+
+ //
+ // This is the first Default Logon entry. If this UID is not
+ // in the table then this is the one to use.
+ //
+
+ DebugTrace(-1, Dbg, " ... DefaultLogon %lx\n", Logon );
+ return Logon;
+ }
+
+ LogonQueueEntry = Logon->Next.Flink;
+ }
+
+ ASSERT( FALSE && "Couldn't find the Id" );
+
+ DebugTrace(-1, Dbg, " ... DefaultLogon NULL\n", 0 );
+ return NULL;
+}
+
+
+PLOGON
+FindUserByName(
+ IN PUNICODE_STRING UserName
+ )
+/*++
+
+Routine Description:
+
+ This routine searches the LogonList for the user entry corresponding
+ to Username.
+
+ Note: Rcb must be held to prevent LogonList being changed.
+
+Arguments:
+
+ UserName - The user name to find.
+
+Return Value:
+
+ If found, a pointer to the logon structure
+ NULL, if no match
+
+--*/
+{
+ PLIST_ENTRY LogonQueueEntry = LogonList.Flink;
+ PLOGON Logon;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "FindUserByName...\n", 0);
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", UserName);
+
+ while ( LogonQueueEntry != &LogonList ) {
+
+ Logon = CONTAINING_RECORD( LogonQueueEntry, LOGON, Next );
+
+ if ( RtlEqualUnicodeString( UserName, &Logon->UserName, TRUE ) ) {
+ DebugTrace(-1, Dbg, " ... %x\n", Logon );
+ return Logon;
+ }
+
+ LogonQueueEntry = Logon->Next.Flink;
+ }
+
+ DebugTrace(-1, Dbg, " ... NULL\n", 0 );
+ return NULL;
+}
+
+
+LARGE_INTEGER
+GetUid(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the effective UID to be used for this create.
+
+Arguments:
+
+ SubjectSecurityContext - Supplies the information from IrpSp.
+
+Return Value:
+
+ None
+
+--*/
+{
+ LARGE_INTEGER LogonId;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetUid ... \n", 0);
+
+
+ // Is the thread currently impersonating someone else?
+
+ if (SubjectSecurityContext->ClientToken != NULL) {
+
+ //
+ // If its impersonating someone that is logged in locally then use
+ // the local id.
+ //
+
+ SeQueryAuthenticationIdToken(SubjectSecurityContext->ClientToken, (PLUID)&LogonId);
+
+ if (FindUser(&LogonId, TRUE) == NULL) {
+
+ //
+ // Not logged on locally, use the processes LogonId so that the
+ // gateway will work.
+ //
+
+ SeQueryAuthenticationIdToken(SubjectSecurityContext->PrimaryToken, (PLUID)&LogonId);
+ }
+
+ } else {
+
+ //
+ // Use the processes LogonId
+ //
+
+ SeQueryAuthenticationIdToken(SubjectSecurityContext->PrimaryToken, (PLUID)&LogonId);
+ }
+
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", LogonId.HighPart);
+ DebugTrace(-1, Dbg, " ->UserUidLow = %08lx\n", LogonId.LowPart);
+
+ return LogonId;
+}
+
+
+VOID
+FreeLogon(
+ IN PLOGON Logon
+ )
+
+/*++
+
+Routine Description:
+
+ This routine free's all the strings inside Logon and the structure itself.
+
+Arguments:
+
+ IN PLOGON Logon
+
+Return Value:
+
+ None
+
+--*/
+{
+ PLIST_ENTRY pListEntry;
+ PNDS_SECURITY_CONTEXT pContext;
+
+ PAGED_CODE();
+
+ if ((Logon == NULL) ||
+ (Logon == &Guest)) {
+ return;
+ }
+
+ if ( Logon->UserName.Buffer != NULL ) {
+ FREE_POOL( Logon->UserName.Buffer );
+ }
+
+ if ( Logon->PassWord.Buffer != NULL ) {
+ FREE_POOL( Logon->PassWord.Buffer );
+ }
+
+ if ( Logon->ServerName.Buffer != NULL ) {
+ FREE_POOL( Logon->ServerName.Buffer );
+ }
+
+ while ( !IsListEmpty(&Logon->NdsCredentialList) ) {
+
+ pListEntry = RemoveHeadList( &Logon->NdsCredentialList );
+ pContext = CONTAINING_RECORD(pListEntry, NDS_SECURITY_CONTEXT, Next );
+ FreeNdsContext( pContext );
+
+ }
+
+ ExDeleteResource( &Logon->CredentialListResource );
+ FREE_POOL( Logon );
+}
+
+
+NTSTATUS
+Logon(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the username and password supplied and makes
+ them the default to be used for all connections.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGON Logon = NULL;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ UNICODE_STRING ServerName;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Logon\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.Logon.UserName)) +
+ InputBuffer->Parameters.Logon.UserNameLength +
+ InputBuffer->Parameters.Logon.PasswordLength +
+ InputBuffer->Parameters.Logon.ServerNameLength +
+ InputBuffer->Parameters.Logon.ReplicaAddrLength) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ Logon = ALLOCATE_POOL(NonPagedPool,sizeof(LOGON));
+ if (Logon == NULL) {
+ try_return( Status = STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ RtlZeroMemory(Logon, sizeof(LOGON));
+ Logon->NodeTypeCode = NW_NTC_LOGON;
+ Logon->NodeByteSize = sizeof(LOGON);
+ InitializeListHead( &Logon->NdsCredentialList );
+ ExInitializeResource( &Logon->CredentialListResource );
+
+ Status = SetUnicodeString(&Logon->UserName,
+ InputBuffer->Parameters.Logon.UserNameLength,
+ InputBuffer->Parameters.Logon.UserName);
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( Status );
+ }
+
+ Status = SetUnicodeString(&Logon->PassWord,
+ InputBuffer->Parameters.Logon.PasswordLength,
+ (PWCHAR)
+ ((PUCHAR)InputBuffer->Parameters.Logon.UserName +
+ InputBuffer->Parameters.Logon.UserNameLength));
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( Status );
+ }
+
+ ServerName.Buffer =
+ (PWCHAR)
+ ((PUCHAR)InputBuffer->Parameters.Logon.UserName +
+ InputBuffer->Parameters.Logon.UserNameLength +
+ InputBuffer->Parameters.Logon.PasswordLength);
+
+ ServerName.Length =
+ (USHORT)InputBuffer->Parameters.Logon.ServerNameLength;
+
+ ServerName.MaximumLength =
+ (USHORT)InputBuffer->Parameters.Logon.ServerNameLength;
+
+ if ( ServerName.Length &&
+ ServerName.Buffer[0] != L'*' ) {
+
+ //
+ // Only set this as the preferred server if it's not
+ // a default tree. Default tree requests start with a '*'.
+ //
+
+ Status = SetUnicodeString(&Logon->ServerName,
+ ServerName.Length,
+ ServerName.Buffer );
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( Status );
+ }
+ }
+
+ //
+ // Store the unique userid in both unicode and large integer form
+ // the unicode form is used as a prefix to the servername in all
+ // paths so that each userid gets their own connection to the server.
+ //
+
+ *((PLUID)(&Logon->UserUid)) = InputBuffer->Parameters.Logon.LogonId;
+
+ // Save Uid for CreateScb
+
+ *((PLUID)(&IrpContext->Specific.Create.UserUid)) =
+ InputBuffer->Parameters.Logon.LogonId;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if (NT_SUCCESS(Status)) {
+
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", &Logon->UserName );
+ DebugTrace( 0, Dbg, " ->PassWord = %wZ\n", &Logon->PassWord );
+
+ if ( ServerName.Length && ServerName.Buffer[0] == L'*' ) {
+ DebugTrace( 0, Dbg, " ->DefaultTree = %wZ\n", &ServerName );
+ } else {
+ DebugTrace( 0, Dbg, " ->ServerName = %wZ\n", &Logon->ServerName );
+ }
+
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", Logon->UserUid.HighPart);
+ DebugTrace( 0, Dbg, " ->UserUidLow = %08lx\n", Logon->UserUid.LowPart);
+
+ InsertHeadList( &LogonList, &Logon->Next );
+ NwReleaseRcb( &NwRcb );
+
+ if ( ServerName.Length &&
+ ServerName.Buffer[0] != L'*' ) {
+
+ PSCB Scb;
+
+ // See if we can login as this user.
+
+ Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &ServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // CreateScb has already boosted the reference count
+ // because this is a preferred server so it will not go
+ // away. We need to dereference it here because there is
+ // no handle associated with the CreateScb
+ //
+
+ NwDereferenceScb(Scb->pNpScb);
+ }
+
+ }
+
+ if ( ServerName.Length &&
+ ServerName.Buffer[0] == L'*' ) {
+
+ PSCB Scb;
+ BOOL SetContext;
+ UINT ContextLength;
+ UNICODE_STRING DefaultContext;
+ IPXaddress *ReplicaAddr;
+
+ //
+ // Ok, this is a little confusing. On Login, the provider can
+ // specify the address of the replica that we should use to log
+ // in. If this is the case, then we do pre-connect that replica.
+ // Otherwise, we do the standard login to any replica. The
+ // reason for this is that standard replica location uses the
+ // bindery and doesn't always get us the nearest dir server.
+ //
+
+ if ( InputBuffer->Parameters.Logon.ReplicaAddrLength ==
+ sizeof( TDI_ADDRESS_IPX ) ) {
+
+ ReplicaAddr = (IPXaddress*)
+ ((PUCHAR) InputBuffer->Parameters.Logon.UserName +
+ InputBuffer->Parameters.Logon.UserNameLength +
+ InputBuffer->Parameters.Logon.PasswordLength +
+ InputBuffer->Parameters.Logon.ServerNameLength);
+
+ CreateScb( &Scb,
+ IrpContext,
+ NULL, // anonymous create
+ ReplicaAddr, // nearest replica add
+ NULL, // no user name
+ NULL, // no password
+ TRUE, // defer the login
+ FALSE ); // we are not deleting the connection
+
+ }
+
+ //
+ // Set if this includes a default context.
+ //
+
+ ServerName.Buffer += 1;
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.MaximumLength -= sizeof( WCHAR );
+
+ SetContext = FALSE;
+ ContextLength = 0;
+
+ while ( ContextLength < ServerName.Length / sizeof( WCHAR ) ) {
+
+ if ( ServerName.Buffer[ContextLength] == L'\\' ) {
+
+ SetContext = TRUE;
+
+ ContextLength++;
+
+ //
+ // Skip any leading periods.
+ //
+
+ if ( ServerName.Buffer[ContextLength] == L'.' ) {
+
+ DefaultContext.Buffer = &ServerName.Buffer[ContextLength + 1];
+ ServerName.Length -= sizeof ( WCHAR ) ;
+ ServerName.MaximumLength -= sizeof ( WCHAR );
+
+ } else {
+
+ DefaultContext.Buffer = &ServerName.Buffer[ContextLength];
+
+ }
+
+ ContextLength *= sizeof( WCHAR );
+ DefaultContext.Length = ServerName.Length - ContextLength;
+ DefaultContext.MaximumLength = ServerName.MaximumLength - ContextLength;
+
+ ServerName.Length -= ( DefaultContext.Length + sizeof( WCHAR ) );
+ ServerName.MaximumLength -= ( DefaultContext.Length + sizeof( WCHAR ) );
+
+ }
+
+ ContextLength++;
+ }
+
+ //
+ // Verify that this context is valid before we acquire
+ // the credentials and really set the context.
+ //
+
+ if ( SetContext ) {
+
+ Status = NdsVerifyContext( IrpContext, &ServerName, &DefaultContext );
+
+ if ( !NT_SUCCESS( Status )) {
+ SetContext = FALSE;
+ }
+
+ }
+
+ //
+ // Generate the credential shell for the default tree and
+ // set the context if appropriate.
+ //
+
+ Status = NdsLookupCredentials( &ServerName,
+ Logon,
+ &pNdsContext,
+ CREDENTIAL_WRITE,
+ TRUE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Set the context. It doesn't matter if the
+ // credential is locked or not.
+ //
+
+ if ( SetContext ) {
+
+ RtlCopyUnicodeString( &pNdsContext->CurrentContext,
+ &DefaultContext );
+ DebugTrace( 0, Dbg, "Default Context: %wZ\n", &DefaultContext );
+ }
+
+ NwReleaseCredList( Logon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "Default Tree: %wZ\n", &ServerName );
+
+ Status = NdsCreateTreeScb( IrpContext,
+ &Scb,
+ &ServerName,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if (NT_SUCCESS(Status)) {
+ NwDereferenceScb(Scb->pNpScb);
+ }
+ }
+ }
+
+ //
+ // No login requested.
+ //
+
+ } else {
+
+ FreeLogon( Logon );
+ NwReleaseRcb( &NwRcb );
+
+ }
+
+
+ DebugTrace(-1, Dbg, "Logon %lx\n", Status);
+ return Status;
+}
+
+
+NTSTATUS
+Logoff(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the username back to guest and removes the password.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ BOOLEAN Locked = FALSE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ LARGE_INTEGER User;
+ PLOGON Logon;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Logoff...\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ *((PLUID)(&User)) = InputBuffer->Parameters.Logoff.LogonId;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ Locked = TRUE;
+
+ Logon = FindUser(&User, TRUE);
+
+ if ( Logon != NULL ) {
+
+ LARGE_INTEGER Uid = Logon->UserUid;
+
+ //
+ // We have found the right user.
+ //
+
+ ASSERT( Logon != &Guest);
+
+ NwReleaseRcb( &NwRcb );
+ Locked = FALSE;
+
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", &Logon->UserName );
+ DebugTrace( 0, Dbg, " ->ServerName = %wZ\n", &Logon->ServerName );
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", Logon->UserUid.HighPart);
+ DebugTrace( 0, Dbg, " ->UserUidLow = %08lx\n", Logon->UserUid.LowPart);
+
+
+ //
+ // Invalidating all the handles for this user will also cause logoffs
+ // to all the servers in question.
+ //
+
+ NwInvalidateAllHandles(&Uid, IrpContext);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ Locked = TRUE;
+
+ Logon = FindUser(&User, TRUE);
+
+ if (Logon != NULL) {
+ RemoveEntryList( &Logon->Next );
+ FreeLogon( Logon );
+ } else {
+ ASSERT( FALSE && "Double logoff!");
+ }
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+try_exit:NOTHING;
+ } finally {
+ if (Locked == TRUE ) {
+ NwReleaseRcb( &NwRcb );
+ }
+ }
+
+ DebugTrace(-1, Dbg, "Logoff %lx\n", Status);
+
+ return Status;
+}
+
+NTSTATUS
+UpdateUsersPassword(
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ OUT PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine updates the cached password for a given user.
+ If the named user is not logged in, an error is returned.
+
+Arguments:
+
+ UserName - Supplies the name of the user
+
+ Password - Supplies the new password
+
+ Uid - Returns the LUID of the updated user.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PLOGON Logon;
+ NTSTATUS Status;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ Logon = FindUserByName( UserName );
+
+ if ( Logon != NULL ) {
+
+ if ( Logon->PassWord.Buffer != NULL ) {
+ FREE_POOL( Logon->PassWord.Buffer );
+ }
+
+ Status = SetUnicodeString(
+ &Logon->PassWord,
+ Password->Length,
+ Password->Buffer );
+
+ *Uid = Logon->UserUid;
+
+ } else {
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ NwReleaseRcb( &NwRcb );
+ return( Status );
+
+}
+
+NTSTATUS
+UpdateServerPassword(
+ PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING ServerName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine updates the cached password for a named server connection.
+ If the server does not exist in the server table, an error is returned.
+
+Arguments:
+
+ ServerName - Supplies the name of the server
+
+ UserName - Supplies the name of the user
+
+ Password - Supplies the new password
+
+ Uid - The LUID of the user.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ UNICODE_STRING UidServer;
+ NTSTATUS Status;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PVOID Buffer;
+
+ Status = MakeUidServer(
+ &UidServer,
+ Uid,
+ ServerName );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = %wZ\n", &UidServer );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServer, 0 );
+
+ if ( PrefixEntry != NULL ) {
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+ pNpScb = pScb->pNpScb;
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the RCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ } else {
+
+ NwReleaseRcb( &NwRcb );
+ FREE_POOL(UidServer.Buffer);
+ return( STATUS_BAD_NETWORK_PATH );
+ }
+
+ IrpContext->pNpScb = pNpScb;
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Free the old username password, allocate a new one.
+ //
+
+ if ( pScb->UserName.Buffer != NULL ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ }
+
+ Buffer = ALLOCATE_POOL_EX( NonPagedPool, UserName->Length + Password->Length );
+
+ pScb->UserName.Buffer = Buffer;
+ pScb->UserName.Length = pScb->UserName.MaximumLength = UserName->Length;
+ RtlMoveMemory( pScb->UserName.Buffer, UserName->Buffer, UserName->Length );
+
+ pScb->Password.Buffer = (PWCHAR)((PCHAR)Buffer + UserName->Length);
+ pScb->Password.Length = pScb->Password.MaximumLength = Password->Length;
+ RtlMoveMemory( pScb->Password.Buffer, Password->Buffer, Password->Length );
+
+ FREE_POOL(UidServer.Buffer);
+
+ return( STATUS_SUCCESS );
+}
+
diff --git a/private/nw/rdr/sources b/private/nw/rdr/sources
new file mode 100644
index 000000000..b4e3f30e1
--- /dev/null
+++ b/private/nw/rdr/sources
@@ -0,0 +1,90 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=nwrdr
+
+TARGETNAME=nwrdr
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DRIVER
+TARGETLIBS=.\*\nw4crypt.lib \
+ .\*\rsa32.lib \
+ $(_NTROOT)\public\sdk\lib\*\tdi.lib
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+C_DEFINES=$(C_DEFINES) -D_PNP_POWER=1
+
+!IF "$(QFE_BUILD)" != "1"
+NET_C_DEFINES=-DNWDBG=1
+!ELSE
+NET_C_DEFINES=-DNWDBG=1 -DQFE_BUILD=1
+!ENDIF
+
+INCLUDES=..\inc;$(_NTROOT)\private\inc;$(_NTROOT)\private\ntos\inc
+
+SOURCES=Attach.c \
+ Cache.c \
+ Callback.c \
+ Cleanup.c \
+ Close.c \
+ Convert.c \
+ Create.c \
+ Data.c \
+ Debug.c \
+ Deviosup.c \
+ Dir.c \
+ Encrypt.c \
+ Errorlog.c \
+ Except.c \
+ Exchange.c \
+ Filobsup.c \
+ Fileinfo.c \
+ Fsctl.c \
+ FspDisp.c \
+ Init.c \
+ Ipx.c \
+ Lock.c \
+ LockCode.c \
+ NwRdr.rc \
+ Pid.c \
+ Read.c \
+ Scavengr.c \
+ Security.c \
+ String.c \
+ Strucsup.c \
+ Timer.c \
+ Util.c \
+ VolInfo.c \
+ Workque.c \
+ Write.c \
+ Create4.c \
+ Fragex.c \
+ Ndsfsctl.c \
+ Ndslogin.c \
+ Ndsread.c
+
+PRECOMPILED_INCLUDE=procs.h
+PRECOMPILED_PCH=procs.pch
+PRECOMPILED_OBJ=procs.obj
diff --git a/private/nw/rdr/string.c b/private/nw/rdr/string.c
new file mode 100644
index 000000000..c2136c3e0
--- /dev/null
+++ b/private/nw/rdr/string.c
@@ -0,0 +1,378 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ string.c
+
+Abstract:
+
+ This module implements the string routines needed for the NT redirector
+
+Author:
+
+ Colin Watson (ColinW) 02-Apr-1993
+
+Revision History:
+
+ 14-Jun-1990 LarryO
+
+ Created for Lanman Redirector
+
+ 02-Apr-1993 ColinW
+
+ Modified for NwRdr
+
+--*/
+
+#include "Procs.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, DuplicateStringWithString )
+#pragma alloc_text( PAGE, DuplicateUnicodeStringWithString )
+#pragma alloc_text( PAGE, SetUnicodeString )
+#pragma alloc_text( PAGE, MergeStrings )
+#endif
+
+
+NTSTATUS
+DuplicateStringWithString (
+ OUT PSTRING DestinationString,
+ IN PSTRING SourceString,
+ IN POOL_TYPE PoolType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine duplicates a supplied input string, storing the result
+ of the duplication in the supplied string. The maximumlength of the
+ new string is determined by the length of the SourceString.
+
+
+Arguments:
+
+ OUT PSTRING DestinationString - Returns the filled in string.
+ IN PSTRING SourceString - Supplies the string to duplicate
+ IN POOLTYPE PoolType - Supplies the type of pool (PagedPool or
+ NonPagedPool)
+Return Value:
+
+ NTSTATUS - Status of resulting operation
+ If !NT_SUCCESS then DestinationString->Buffer == NULL
+--*/
+
+{
+ PAGED_CODE();
+
+ DestinationString->Buffer = NULL;
+
+ try {
+
+ if (SourceString->Length != 0) {
+ //
+ // Allocate pool to hold the buffer (contents of the string)
+ //
+
+ DestinationString->Buffer = (PSZ )ALLOCATE_POOL(PoolType,
+ SourceString->Length);
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+
+ if (DestinationString->Buffer == NULL && SourceString->Length != 0) {
+
+ //
+ // The allocation failed, return failure.
+ //
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ DestinationString->MaximumLength = SourceString->Length;
+
+ //
+ // Copy the source string into the newly allocated
+ // destination string
+ //
+
+ RtlCopyString(DestinationString, SourceString);
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+DuplicateUnicodeStringWithString (
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN POOL_TYPE PoolType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine duplicates a supplied input string, storing the result
+ of the duplication in the supplied string. The maximumlength of the
+ new string is determined by the length of the SourceString.
+
+
+Arguments:
+
+ OUT PSTRING DestinationString - Returns the filled in string.
+ IN PSTRING SourceString - Supplies the string to duplicate
+ IN POOLTYPE PoolType - Supplies the type of pool (PagedPool or
+ NonPagedPool)
+Return Value:
+
+ NTSTATUS - Status of resulting operation
+ If !NT_SUCCESS then DestinationString->Buffer == NULL
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DestinationString->Buffer = NULL;
+
+ try {
+
+ if (SourceString->Length != 0) {
+ //
+ // Allocate pool to hold the buffer (contents of the string)
+ //
+
+ DestinationString->Buffer = (WCHAR *)ALLOCATE_POOL(PoolType,
+ SourceString->Length);
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+
+ if (DestinationString->Buffer == NULL && SourceString->Length != 0) {
+
+ //
+ // The allocation failed, return failure.
+ //
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ DestinationString->MaximumLength = SourceString->Length;
+
+ //
+ // Copy the source string into the newly allocated
+ // destination string
+ //
+
+ RtlCopyUnicodeString(DestinationString, SourceString);
+
+ return STATUS_SUCCESS;
+
+}
+
+#if 0
+
+VOID
+CopyUnicodeStringToUnicode (
+ OUT PVOID *Destination,
+ IN PUNICODE_STRING Source,
+ IN BOOLEAN AdjustPointer
+ )
+
+/*++
+
+Routine Description:
+ This routine copies the specified source string onto the destination
+ asciiz string.
+
+Arguments:
+
+ OUT PUCHAR Destination, - Supplies a pointer to the destination
+ buffer for the string.
+ IN PSTRING String - Supplies the source string.
+ IN BOOLEAN AdjustPointer - If TRUE, increment destination pointer
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ RtlCopyMemory((*Destination), (Source)->Buffer, (Source)->Length);
+ if (AdjustPointer) {
+ ((PCHAR)(*Destination)) += ((Source)->Length);
+ }
+}
+
+
+NTSTATUS
+CopyUnicodeStringToAscii (
+ OUT PUCHAR *Destination,
+ IN PUNICODE_STRING Source,
+ IN BOOLEAN AdjustPointer,
+ IN USHORT MaxLength
+ )
+/*++
+
+Routine Description:
+
+ This routine copies the specified source string onto the destination
+ asciiz string.
+
+Arguments:
+
+ OUT PUCHAR Destination, - Supplies the destination asciiz string.
+ IN PUNICODE_STRING String - Supplies the source string.
+ IN BOOLEAN AdjustPointer - If TRUE, increment destination pointer
+
+Return Value:
+
+ Status of conversion.
+--*/
+{
+ ANSI_STRING DestinationString;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DestinationString.Buffer = (*Destination);
+
+ DestinationString.MaximumLength = (USHORT)(MaxLength);
+
+ Status = RtlUnicodeStringToOemString(&DestinationString, (Source), FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ if (AdjustPointer) {
+ (*Destination) += DestinationString.Length;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+#endif
+
+
+NTSTATUS
+SetUnicodeString (
+ IN PUNICODE_STRING Destination,
+ IN ULONG Length,
+ IN PWCHAR Source
+ )
+/*++
+
+Routine Description:
+
+ This routine copies the specified source string onto the destination
+ UNICODE string allocating the buffer.
+
+Arguments:
+
+
+Return Value:
+
+ Status of conversion.
+--*/
+{
+ UNICODE_STRING Temp;
+
+ PAGED_CODE();
+
+ Destination->Buffer = NULL;
+ Destination->Length = 0;
+ Destination->MaximumLength = 0;
+
+ if (Length == 0) {
+ return STATUS_SUCCESS;
+ }
+
+ Temp.MaximumLength =
+ Temp.Length = (USHORT )Length;
+ Temp.Buffer = Source;
+
+ Destination->Buffer =
+ ALLOCATE_POOL(NonPagedPool,
+ Temp.MaximumLength+sizeof(WCHAR));
+
+ if (Destination->Buffer == NULL) {
+ Error(EVENT_NWRDR_RESOURCE_SHORTAGE, STATUS_INSUFFICIENT_RESOURCES, NULL, 0, 0);
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ Destination->MaximumLength = (USHORT)Length;
+
+ RtlCopyUnicodeString(Destination, &Temp);
+
+ Destination->Buffer[(Destination->Length/sizeof(WCHAR))] = UNICODE_NULL;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+MergeStrings(
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING S1,
+ IN PUNICODE_STRING S2,
+ IN ULONG Type
+ )
+/*++
+
+Routine Description:
+
+ This routine Allocates space for Destination.Buffer and copies S1 followed
+ by S2 into the buffer.
+
+ Raises status if couldn't allocate buffer
+
+Arguments:
+
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING S1,
+ IN PUNICODE_STRING S2,
+ IN ULONG Type - PagedPool or NonPagedPool
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ Destination->MaximumLength = S1->Length + S2->Length;
+ Destination->Length = S1->Length + S2->Length;
+
+ Destination->Buffer = ALLOCATE_POOL_EX( Type, Destination->MaximumLength );
+
+ RtlCopyMemory( Destination->Buffer,
+ S1->Buffer,
+ S1->Length);
+
+ RtlCopyMemory( (PUCHAR)Destination->Buffer + S1->Length,
+ S2->Buffer,
+ S2->Length);
+ return;
+}
diff --git a/private/nw/rdr/strucsup.c b/private/nw/rdr/strucsup.c
new file mode 100644
index 000000000..3595cc709
--- /dev/null
+++ b/private/nw/rdr/strucsup.c
@@ -0,0 +1,3127 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ strucsup.c
+
+Abstract:
+
+ This module implements the Netware Redirector structure support routines.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Feb-1993
+
+Revision History:
+
+--*/
+#include "procs.h"
+
+BOOLEAN
+GetLongNameSpaceForVolume(
+ IN PIRP_CONTEXT IrpContext,
+ IN UNICODE_STRING ShareName,
+ OUT PCHAR VolumeLongNameSpace,
+ OUT PCHAR VolumeNumber
+ );
+
+CHAR
+GetNewDriveNumber (
+ IN PSCB Scb
+ );
+
+VOID
+FreeDriveNumber(
+ IN PSCB Scb,
+ IN CHAR DriveNumber
+ );
+
+#define Dbg (DEBUG_TRACE_STRUCSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwInitializeRcb )
+#pragma alloc_text( PAGE, NwDeleteRcb )
+#pragma alloc_text( PAGE, NwCreateIcb )
+#pragma alloc_text( PAGE, NwDeleteIcb )
+#pragma alloc_text( PAGE, NwVerifyIcb )
+#pragma alloc_text( PAGE, NwVerifyIcbSpecial )
+#pragma alloc_text( PAGE, NwInvalidateAllHandlesForScb )
+#pragma alloc_text( PAGE, NwVerifyScb )
+#pragma alloc_text( PAGE, NwCreateFcb )
+#pragma alloc_text( PAGE, NwFindFcb )
+#pragma alloc_text( PAGE, NwDereferenceFcb )
+#pragma alloc_text( PAGE, NwFindVcb )
+#pragma alloc_text( PAGE, NwCreateVcb )
+#pragma alloc_text( PAGE, NwReopenVcbHandlesForScb )
+#pragma alloc_text( PAGE, NwReopenVcbHandle )
+#ifdef NWDBG
+#pragma alloc_text( PAGE, NwReferenceVcb )
+#endif
+#pragma alloc_text( PAGE, NwDereferenceVcb )
+#pragma alloc_text( PAGE, NwCleanupVcb )
+#pragma alloc_text( PAGE, GetLongNameSpaceForVolume )
+#pragma alloc_text( PAGE, IsFatNameValid )
+#pragma alloc_text( PAGE, GetNewDriveNumber )
+#pragma alloc_text( PAGE, FreeDriveNumber )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwInvalidateAllHandles )
+#pragma alloc_text( PAGE1, NwCloseAllVcbs )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+VOID
+NwInitializeRcb (
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes new Rcb record.
+
+Arguments:
+
+ Rcb - Supplies the address of the Rcb record being initialized.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwInitializeRcb, Rcb = %08lx\n", (ULONG)Rcb);
+
+ //
+ // We start by first zeroing out all of the RCB, this will guarantee
+ // that any stale data is wiped clean.
+ //
+
+ RtlZeroMemory( Rcb, sizeof(RCB) );
+
+ //
+ // Set the node type code, node byte size, and reference count.
+ //
+
+ Rcb->NodeTypeCode = NW_NTC_RCB;
+ Rcb->NodeByteSize = sizeof(RCB);
+ Rcb->OpenCount = 0;
+
+ //
+ // Initialize the resource variable for the RCB.
+ //
+
+ ExInitializeResource( &Rcb->Resource );
+
+ //
+ // Initialize the server name and file name tables.
+ //
+
+ RtlInitializeUnicodePrefix( &Rcb->ServerNameTable );
+ RtlInitializeUnicodePrefix( &Rcb->VolumeNameTable );
+ RtlInitializeUnicodePrefix( &Rcb->FileNameTable );
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwInitializeRcb -> VOID\n", 0);
+
+ return;
+}
+
+
+VOID
+NwDeleteRcb (
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes the RCB record from our in-memory data
+ structures. It also will remove all associated underlings
+ (i.e., FCB records).
+
+Arguments:
+
+ Rcb - Supplies the Rcb to be removed
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDeleteRcb, Rcb = %08lx\n", (ULONG)Rcb);
+
+ //
+ // Uninitialize the resource variable for the RCB.
+ //
+
+ ExDeleteResource( &Rcb->Resource );
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwDeleteRcb -> VOID\n", 0);
+
+ return;
+}
+
+
+PICB
+NwCreateIcb (
+ IN USHORT Type,
+ IN PVOID Associate
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initialize a new ICB. The ICB is
+ inserted into the FCB's list.
+
+ *** This routine must be called with the RCB held exclusively.
+
+Arguments:
+
+ Type - The type of ICB this will be.
+
+ Associate - A pointer to an associated data structure.
+ It will be a FCB, DCB, or SCB.
+
+Return Value:
+
+ ICB - A pointer to the newly created ICB.
+
+ If memory allocation fails, this routine will raise an exception.
+
+--*/
+
+{
+ PICB Icb;
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ Icb = ALLOCATE_POOL_EX( NonPagedPool, sizeof( ICB ) );
+
+ RtlZeroMemory( Icb, sizeof( ICB ) );
+
+ Icb->NodeTypeCode = Type;
+ Icb->NodeByteSize = sizeof( ICB );
+ Icb->State = ICB_STATE_OPEN_PENDING;
+ Icb->Pid = (UCHAR)INVALID_PID;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if ( Type == NW_NTC_ICB ) {
+
+ PFCB Fcb = (PFCB)Associate;
+
+ //
+ // Insert this ICB on the list of ICBs for this FCB.
+ //
+
+ InsertTailList( &Fcb->IcbList, &Icb->ListEntry );
+ ++Fcb->IcbCount;
+ Icb->SuperType.Fcb = Fcb;
+ Icb->NpFcb = Fcb->NonPagedFcb;
+
+ Fcb->Vcb->OpenFileCount++;
+ Scb = Fcb->Scb;
+
+ Scb->OpenFileCount++;
+
+ } else if ( Type == NW_NTC_ICB_SCB ) {
+
+ Scb = (PSCB)Associate;
+
+ //
+ // Insert this ICB on the list of ICBs for this SCB.
+ //
+
+ InsertTailList( &Scb->IcbList, &Icb->ListEntry );
+ ++Scb->IcbCount;
+ Icb->SuperType.Scb = Scb;
+
+ } else {
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ NwReferenceScb( Scb->pNpScb );
+ return( Icb );
+}
+
+
+VOID
+NwDeleteIcb (
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes an ICB in the OPEN_PENDING state.
+
+ *** The IRP context must be at the head of the SCB queue when
+ this routine is called.
+
+Arguments:
+
+ Icb - A pointer the ICB to delete.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFCB Fcb;
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ //
+ // Acquire the lock to protect the ICB list.
+ //
+ DebugTrace( 0, DEBUG_TRACE_ICBS, "NwDeleteIcb, Icb = %08lx\n", (ULONG)Icb);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ RemoveEntryList( &Icb->ListEntry );
+
+ if ( Icb->NodeTypeCode == NW_NTC_ICB ) {
+
+ Fcb = Icb->SuperType.Fcb;
+ Scb = Fcb->Scb;
+
+ //
+ // Decrement the open file count for the VCB. Note that the ICB
+ // only reference the VCB indirectly via the FCB, so that we do
+ // not dereference the VCB here.
+ //
+
+ --Fcb->Vcb->OpenFileCount;
+ --Scb->OpenFileCount;
+
+ //
+ // Dereference the FCB. This frees the FCB if
+ // this was the last ICB for the FCB.
+ //
+
+ NwDereferenceFcb( IrpContext, Fcb );
+
+ } else if ( Icb->NodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ Scb = Icb->SuperType.Scb;
+
+ //
+ // Decrement of OpenIcb count on the SCB.
+ //
+
+ Scb->IcbCount--;
+
+ } else {
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+ //
+ // Free the query template buffers.
+ //
+
+ RtlFreeOemString( &Icb->NwQueryTemplate );
+
+ if ( Icb->UQueryTemplate.Buffer != NULL ) {
+ FREE_POOL( Icb->UQueryTemplate.Buffer );
+ }
+
+ //
+ // Try and gracefully catch a 16 bit app closing a
+ // handle to the server and wipe the connection as
+ // soon as possible. This only applies to bindery
+ // authenticated connections because in NDS land,
+ // we handle the licensing of the connection
+ // dynamically.
+ //
+
+ if ( ( Scb->pNpScb->Reference == 1 ) &&
+ ( Icb->NodeTypeCode == NW_NTC_ICB_SCB ) &&
+ ( !Icb->IsTreeHandle ) &&
+ ( IrpContext != NULL ) &&
+ ( Scb->UserName.Length != 0 ) )
+ {
+ LARGE_INTEGER Now;
+ KeQuerySystemTime( &Now );
+
+ DebugTrace( 0, Dbg, "Quick disconnecting 16-bit app.\n", 0 );
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( Scb->OpenFileCount == 0 &&
+ Scb->pNpScb->State != SCB_STATE_RECONNECT_REQUIRED &&
+ !Scb->PreferredServer ) {
+
+ NwLogoffAndDisconnect( IrpContext, Scb->pNpScb);
+ }
+
+ Now.QuadPart += ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwDereferenceScb( Scb->pNpScb );
+ DisconnectTimedOutScbs(Now) ;
+ CleanupScbs(Now);
+
+ } else {
+
+ NwDereferenceScb( Scb->pNpScb );
+
+ }
+ FREE_POOL( Icb );
+ NwReleaseRcb( &NwRcb );
+}
+
+VOID
+NwVerifyIcb (
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an ICB is in the opened state.
+ If it is not, the routine raises an exception.
+
+Arguments:
+
+ Icb - A pointer the ICB to verify.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( Icb->State != ICB_STATE_OPENED ) {
+ ExRaiseStatus( STATUS_INVALID_HANDLE );
+ }
+}
+
+VOID
+NwVerifyIcbSpecial (
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an ICB is in the opened state.
+ If it is not, the routine raises an exception.
+
+Arguments:
+
+ Icb - A pointer the ICB to verify.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( (Icb->State != ICB_STATE_OPENED &&
+ Icb->State != ICB_STATE_CLEANED_UP) ) {
+ ExRaiseStatus( STATUS_INVALID_HANDLE );
+ }
+}
+
+
+ULONG
+NwInvalidateAllHandles (
+ PLARGE_INTEGER Uid OPTIONAL,
+ PIRP_CONTEXT IrpContext OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine finds all of the ICB in the system that were created
+ by the user specified by the Logon credentials and marks them
+ invalid.
+
+Arguments:
+
+ Uid - Supplies the userid of the handles to close or NULL if all
+ handles to be invalidated.
+ IrpContext - The Irpcontext to be used for the NwLogoffAndDisconnect
+ call, if appropriate. If this is NULL, it indicates a RAS
+ transition.
+
+Return Value:
+
+ The number of active handles that were closed.
+
+--*/
+
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry, NextScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ ULONG FilesClosed = 0;
+
+ PAGED_CODE();
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ pScb = pNpScb->pScb;
+ if ( pScb != NULL ) {
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the SCB spin lock as we are about to touch nonpaged pool.
+ //
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ if ((Uid == NULL) ||
+ ( pScb->UserUid.QuadPart == (*Uid).QuadPart)) {
+
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ FilesClosed += NwInvalidateAllHandlesForScb( pScb );
+ NwReleaseRcb( &NwRcb );
+
+ if ( IrpContext ) {
+
+ IrpContext->pNpScb = pNpScb;
+ NwLogoffAndDisconnect( IrpContext , pNpScb);
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ } else {
+
+ //
+ // No IrpContext means that a RAS transition has occurred.
+ // Let's try to keep our Netware servers happy if the net
+ // is still attached.
+ //
+
+ PIRP_CONTEXT LocalIrpContext;
+ if (NwAllocateExtraIrpContext(&LocalIrpContext, pNpScb)) {
+
+ // Lock down so that we can send a packet.
+ NwReferenceUnlockableCodeSection();
+
+ LocalIrpContext->pNpScb = pNpScb;
+ NwLogoffAndDisconnect( LocalIrpContext, pNpScb);
+
+ NwAppendToQueueAndWait( LocalIrpContext );
+
+ NwDequeueIrpContext( LocalIrpContext, FALSE );
+ NwDereferenceUnlockableCodeSection ();
+ NwFreeExtraIrpContext( LocalIrpContext );
+
+ }
+
+ //
+ // Clear the LIP data speed.
+ //
+
+ pNpScb->LipDataSpeed = 0;
+ pNpScb->State = SCB_STATE_ATTACHING;
+
+ }
+
+
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ NwDereferenceScb( pNpScb );
+ }
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ return( FilesClosed );
+}
+
+ULONG
+NwInvalidateAllHandlesForScb (
+ PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ This routine finds all of the ICB in for an SCB and marks them
+ invalid.
+
+ *** The caller must own the RCB shared or exclusive.
+
+Arguments:
+
+ SCB - A pointer to the SCB whose files are closed.
+
+Return Value:
+
+ The number of files that were closed.
+
+--*/
+
+{
+ PLIST_ENTRY VcbQueueEntry;
+ PLIST_ENTRY FcbQueueEntry;
+ PLIST_ENTRY IcbQueueEntry;
+ PVCB pVcb;
+ PFCB pFcb;
+ PICB pIcb;
+
+ ULONG FilesClosed = 0;
+
+ PAGED_CODE();
+
+ //
+ // Walk the list of VCBs for this SCB
+ //
+
+ for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &Scb->ScbSpecificVcbQueue;
+ VcbQueueEntry = VcbQueueEntry->Flink ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+ pVcb->Specific.Disk.Handle = (CHAR)-1;
+ }
+
+ //
+ // Walk the list of FCBs and DCSs for this VCB
+ //
+
+ for ( FcbQueueEntry = pVcb->FcbList.Flink;
+ FcbQueueEntry != &pVcb->FcbList;
+ FcbQueueEntry = FcbQueueEntry->Flink ) {
+
+ pFcb = CONTAINING_RECORD( FcbQueueEntry, FCB, FcbListEntry );
+
+ //
+ // Walk the list of ICBs for this FCB or DCB
+ //
+
+ for ( IcbQueueEntry = pFcb->IcbList.Flink;
+ IcbQueueEntry != &pFcb->IcbList;
+ IcbQueueEntry = IcbQueueEntry->Flink ) {
+
+ pIcb = CONTAINING_RECORD( IcbQueueEntry, ICB, ListEntry );
+
+ //
+ // Mark the ICB handle invalid.
+ //
+
+ pIcb->State = ICB_STATE_CLOSE_PENDING;
+ pIcb->HasRemoteHandle = FALSE;
+ FilesClosed++;
+ }
+ }
+ }
+
+ return( FilesClosed );
+}
+
+
+VOID
+NwVerifyScb (
+ IN PSCB Scb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an SCB is in the opened state.
+ If it is not, the routine raises an exception.
+
+Arguments:
+
+ Scb - A pointer the SCB to verify.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( Scb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) {
+ ExRaiseStatus( STATUS_INVALID_HANDLE );
+ }
+}
+
+
+PFCB
+NwCreateFcb (
+ IN PUNICODE_STRING FileName,
+ IN PSCB Scb,
+ IN PVCB Vcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initialize a new FCB. The FCB is
+ inserted into the RCB prefix table.
+
+ *** This routine must be called with the RCB held exclusively.
+
+Arguments:
+
+ FileName - The name of the file to create.
+
+ Scb - A pointer to the SCB for this file.
+
+ Vcb - A pointer to the VCB for the file.
+
+Return Value:
+
+ FCB - A pointer to the newly created DCB.
+
+ If memory allocation fails, this routine will raise an exception.
+
+--*/
+
+{
+ PFCB Fcb;
+ PNONPAGED_FCB NpFcb;
+ PWCH FileNameBuffer;
+ SHORT Length;
+
+ PAGED_CODE();
+
+ Fcb = NULL;
+ NpFcb = NULL;
+
+ try {
+
+ //
+ // Allocate and initialize structures.
+ //
+
+ Fcb = ALLOCATE_POOL_EX(
+ PagedPool,
+ sizeof( FCB ) + FileName->Length + sizeof(WCHAR));
+
+ RtlZeroMemory( Fcb, sizeof( FCB ) );
+ Fcb->NodeTypeCode = NW_NTC_FCB;
+ Fcb->NodeByteSize = sizeof( FCB ) + FileName->Length;
+ Fcb->State = FCB_STATE_OPEN_PENDING;
+
+ InitializeListHead( &Fcb->IcbList );
+
+ Fcb->Vcb = Vcb;
+ Fcb->Scb = Scb;
+
+ FileNameBuffer = (PWCH)(Fcb + 1);
+
+ NpFcb = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NONPAGED_FCB ) );
+ RtlZeroMemory( NpFcb, sizeof( NONPAGED_FCB ) );
+
+ NpFcb->Header.NodeTypeCode = NW_NTC_NONPAGED_FCB;
+ NpFcb->Header.NodeByteSize = sizeof( NONPAGED_FCB );
+
+ NpFcb->Fcb = Fcb;
+ Fcb->NonPagedFcb = NpFcb;
+
+ //
+ // Initialize the resource variable for the FCB.
+ //
+
+ ExInitializeResource( &NpFcb->Resource );
+
+ //
+ // Copy the file name
+ //
+
+ RtlCopyMemory( FileNameBuffer, FileName->Buffer, FileName->Length );
+ Fcb->FullFileName.MaximumLength = FileName->Length;
+ Fcb->FullFileName.Length = FileName->Length;
+ Fcb->FullFileName.Buffer = FileNameBuffer;
+
+ //
+ // The Relative name is normally the full name without the
+ // server and volume name. Also strip the leading backslash.
+ //
+
+ Length = FileName->Length - Vcb->Name.Length - sizeof(L'\\');
+ if ( Length < 0 ) {
+ Length = 0;
+ }
+
+ Fcb->RelativeFileName.Buffer = (PWCH)
+ ((PCHAR)FileNameBuffer + Vcb->Name.Length + sizeof(L'\\'));
+
+ Fcb->RelativeFileName.MaximumLength = Length;
+ Fcb->RelativeFileName.Length = Length;
+
+ //
+ // Insert this file in the prefix table.
+ //
+
+ RtlInsertUnicodePrefix(
+ &NwRcb.FileNameTable,
+ &Fcb->FullFileName,
+ &Fcb->PrefixEntry );
+
+ //
+ // Insert this file into the VCB list, and increment the
+ // file open count.
+ //
+
+ NwReferenceVcb( Vcb );
+
+ InsertTailList(
+ &Vcb->FcbList,
+ &Fcb->FcbListEntry );
+
+ //
+ // Initialize the list of file locks for this FCB.
+ //
+
+ InitializeListHead( &NpFcb->FileLockList );
+ InitializeListHead( &NpFcb->PendingLockList );
+
+ //
+ // Set the long name bit if necessary
+ //
+
+ if ( Fcb->Vcb->Specific.Disk.LongNameSpace != LFN_NO_OS2_NAME_SPACE ) {
+
+ //
+ // OBSCURE CODE POINT
+ //
+ // By default FavourLongNames is not set and we use DOS name
+ // space unless we know we have to use LFN. Reason is if we
+ // start using LFN then DOS apps that dont handle longnames
+ // will give us short names and we are hosed because we are
+ // using LFN NCPs that dont see the short names. Eg. without
+ // the check below, the following will fail (assume mv.exe is
+ // DOS app).
+ //
+ // cd public\longnamedir
+ // mv foo bar
+ //
+ // This is because we will get call with public\longname\foo
+ // and the truncated dir name is not accepted. If user values
+ // case sensitivity, they can set this reg value and we will
+ // use LFN even for short names. They sacrifice the scenario
+ // above.
+ //
+ if ( FavourLongNames || !IsFatNameValid( &Fcb->RelativeFileName ) ) {
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_LONG_NAME );
+ }
+ }
+
+ } finally {
+ if ( AbnormalTermination() ) {
+ if ( Fcb != NULL ) FREE_POOL( Fcb );
+ if ( NpFcb != NULL ) FREE_POOL( NpFcb );
+ }
+ }
+
+ return( Fcb );
+}
+
+
+PFCB
+NwFindFcb (
+ IN PSCB Scb,
+ IN PVCB Vcb,
+ IN PUNICODE_STRING FileName,
+ IN PDCB Dcb OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine find an existing FCB by matching the file name.
+ If a match is find the FCB reference count is incremented.
+ If no match is found an FCB is created.
+
+Arguments:
+
+ Scb - A pointer to the server for this open.
+
+ FileName - The name of the file to find.
+
+ Dcb - A pointer to the DCB for relative opens. If NULL the FileName
+ is an full path name. If non NUL the FileName is relative to
+ this directory.
+
+
+Return Value:
+
+ FCB - A pointer to the found or newly created DCB.
+
+ If memory allocation fails, this routine will raise an exception.
+
+--*/
+
+{
+ PFCB Fcb;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+ UNICODE_STRING FullName;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFindFcb\n", 0);
+ ASSERT( Scb->NodeTypeCode == NW_NTC_SCB );
+
+ if ( Dcb == NULL ) {
+
+ MergeStrings( &FullName,
+ &Scb->UnicodeUid,
+ FileName,
+ PagedPool );
+
+ } else {
+
+ //
+ // Construct full name.
+ //
+
+ FullName.Length = Dcb->FullFileName.Length + FileName->Length + 2;
+ FullName.MaximumLength = FullName.Length;
+ FullName.Buffer = ALLOCATE_POOL_EX( PagedPool, FullName.Length );
+
+ RtlCopyMemory(
+ FullName.Buffer,
+ Dcb->FullFileName.Buffer,
+ Dcb->FullFileName.Length );
+
+ FullName.Buffer[ Dcb->FullFileName.Length / sizeof(WCHAR) ] = L'\\';
+
+ RtlCopyMemory(
+ FullName.Buffer + Dcb->FullFileName.Length / sizeof(WCHAR) + 1,
+ FileName->Buffer,
+ FileName->Length );
+ }
+
+ DebugTrace( 0, Dbg, " ->FullName = ""%wZ""\n", &FullName);
+
+ //
+ // Strip the trailing '\' if there is one.
+ //
+
+ if ( FullName.Buffer[ FullName.Length/sizeof(WCHAR) - 1] == L'\\' ) {
+ FullName.Length -= sizeof(WCHAR);
+ }
+
+ Fcb = NULL;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.FileNameTable, &FullName, 0 );
+
+ if ( Prefix != NULL ) {
+ Fcb = CONTAINING_RECORD( Prefix, FCB, PrefixEntry );
+
+ if ( Fcb->FullFileName.Length != FullName.Length ) {
+
+ //
+ // This was not an exact match. Ignore it.
+ // or
+ // This Fcb is for a share owned by another LogonId.
+ //
+
+ Fcb = NULL;
+ }
+
+ }
+
+ try {
+ if ( Fcb != NULL ) {
+ DebugTrace(0, Dbg, "Found existing FCB = %08lx\n", Fcb);
+ } else {
+ Fcb = NwCreateFcb( &FullName, Scb, Vcb );
+ DebugTrace(0, Dbg, "Created new FCB = %08lx\n", Fcb);
+ }
+ } finally {
+
+ if ( FullName.Buffer != NULL ) {
+ FREE_POOL( FullName.Buffer );
+ }
+
+ NwReleaseRcb( &NwRcb );
+ }
+
+ ASSERT( Fcb == NULL || Fcb->Scb == Scb );
+
+ DebugTrace(-1, Dbg, "NwFindFcb\n", 0);
+ return( Fcb );
+}
+
+
+VOID
+NwDereferenceFcb(
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PFCB Fcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrement the ICB count for an FCB. If the count
+ goes to zero, cleanup the FCB.
+
+ *** This routine must be called with the RCB held exclusively.
+
+Arguments:
+
+ FCB - A pointer to an FCB.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PNONPAGED_FCB NpFcb;
+ PLIST_ENTRY listEntry, nextListEntry;
+ PNW_FILE_LOCK pFileLock;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDereferenceFcb\n", 0);
+ DebugTrace(0, Dbg, "New ICB count = %d\n", Fcb->IcbCount-1 );
+
+ ASSERT( NodeType( Fcb ) == NW_NTC_FCB ||
+ NodeType( Fcb ) == NW_NTC_DCB );
+
+ if ( --Fcb->IcbCount == 0 ) {
+
+ NpFcb = Fcb->NonPagedFcb;
+
+ ASSERT( IsListEmpty( &Fcb->IcbList ) );
+
+ //
+ // If there are outstanding locks, clean them up. This
+ // happens when something causes a remote handle to get
+ // closed before the cleanup routine is called by the
+ // ios on the regular close path.
+ //
+
+ if ( !IsListEmpty( &NpFcb->FileLockList ) ) {
+
+ DebugTrace( 0, Dbg, "Freeing stray locks on FCB %08lx\n", NpFcb );
+
+ for ( listEntry = NpFcb->FileLockList.Flink;
+ listEntry != &NpFcb->FileLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ pFileLock = CONTAINING_RECORD( listEntry,
+ NW_FILE_LOCK,
+ ListEntry );
+
+ RemoveEntryList( listEntry );
+ FREE_POOL( pFileLock );
+ }
+ }
+
+ if ( !IsListEmpty( &NpFcb->PendingLockList ) ) {
+
+ DebugTrace( 0, Dbg, "Freeing stray pending locks on FCB %08lx\n", NpFcb );
+
+ for ( listEntry = NpFcb->PendingLockList.Flink;
+ listEntry != &NpFcb->PendingLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ pFileLock = CONTAINING_RECORD( listEntry,
+ NW_FILE_LOCK,
+ ListEntry );
+
+ RemoveEntryList( listEntry );
+ FREE_POOL( pFileLock );
+ }
+ }
+
+ //
+ // Delete the file now, if it is delete pending.
+ //
+
+ if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE ) ) {
+ NwDeleteFile( IrpContext );
+ }
+
+ //
+ // Remove this file in the prefix table.
+ //
+
+ RtlRemoveUnicodePrefix(
+ &NwRcb.FileNameTable,
+ &Fcb->PrefixEntry );
+
+ //
+ // Remove this file from the SCB list, and decrement the
+ // file open count.
+ //
+
+ RemoveEntryList( &Fcb->FcbListEntry );
+ NwDereferenceVcb( Fcb->Vcb, IrpContext, TRUE );
+
+ //
+ // Delete the resource variable for the FCB.
+ //
+
+ ExDeleteResource( &NpFcb->Resource );
+
+ //
+ // Delete the cache buffer and MDL.
+ //
+
+ if ( NpFcb->CacheBuffer != NULL ) {
+ FREE_POOL( NpFcb->CacheBuffer );
+ FREE_MDL( NpFcb->CacheMdl );
+ }
+
+ //
+ // Finally free the paged and non-paged memory
+ //
+
+ FREE_POOL( Fcb );
+ FREE_POOL( NpFcb );
+ }
+
+ DebugTrace(-1, Dbg, "NwDereferenceFcb\n", 0);
+}
+
+
+PVCB
+NwFindVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection,
+ IN BOOLEAN FindExisting
+ )
+
+/*++
+
+Routine Description:
+
+ This routine looks for a VCB structure. If one is found, it
+ is referenced and a pointer is returned. If no VCB is found, an
+ attempt is made to connect to the named volume and to create a VCB.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context block for this request.
+
+ VolumeName - The minimum name of the volume. This will be in one of
+ the following forms:
+
+ \SERVER\SHARE UNC open server volume
+ \TREE\VOLUME UNC open tree volume in current context
+ \TREE\PATH.TO.VOLUME UNC open distinguished tree volume
+
+ \X:\SERVER\SHARE tree connect server volume
+ \X:\TREE\VOLUME tree connect tree volume in current context
+ \X:\TREE\PATH.TO.VOLUME tree connect distinguished tree volume
+
+ ShareType - The type of the share to find.
+
+ DriveLetter - The drive letter to find. A - Z for drive letter, 1 - 9
+ for LPT ports or 0 if none.
+
+ ExplicitConnection - If TRUE, the caller is make an explicit connection
+ to this Volume. If FALSE, this is an implicit connection made by
+ a UNC operation.
+
+Return Value:
+
+ VCB - Pointer to a found or newly created VCB.
+
+--*/
+{
+ PVCB Vcb = NULL;
+ BOOLEAN OwnRcb = TRUE;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+ UNICODE_STRING UidVolumeName;
+ PNONPAGED_SCB pNpScb = IrpContext->pScb->pNpScb;
+
+ PAGED_CODE();
+
+ UidVolumeName.Buffer = NULL;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ MergeStrings( &UidVolumeName,
+ &IrpContext->pScb->UnicodeUid,
+ VolumeName,
+ PagedPool );
+
+ DebugTrace(+1, Dbg, "NwFindVcb %wZ\n", &UidVolumeName );
+
+ if ( DriveLetter != 0 ) {
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) ||
+ ( DriveLetter >= L'1' && DriveLetter <= L'9' ) );
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ((Vcb != NULL) &&
+ (IrpContext->Specific.Create.UserUid.QuadPart != Vcb->Scb->UserUid.QuadPart )) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ } else {
+
+ //
+ // This is a UNC path. Look up the path name.
+ //
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 );
+
+ if ( Prefix != NULL ) {
+ Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry );
+
+ if ( Vcb->Name.Length != UidVolumeName.Length ) {
+
+ //
+ // This was not an exact match. Ignore it.
+ //
+
+ Vcb = NULL;
+ }
+ }
+ }
+
+ if ( Vcb != NULL ) {
+
+ //
+ // If this is an explicit use to a UNC path, we may find an
+ // existing VCB structure. Mark this structure, and reference it.
+ //
+
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) &&
+ ExplicitConnection ) {
+
+ NwReferenceVcb( Vcb );
+ SetFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ SetFlag( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY );
+
+ //
+ // Count this as an open file on the SCB.
+ //
+
+ ++Vcb->Scb->OpenFileCount;
+ }
+
+ NwReferenceVcb( Vcb );
+ DebugTrace(0, Dbg, "Found existing VCB = %08lx\n", Vcb);
+
+ //
+ // If this VCB is queued to a different SCB as may
+ // happen when we are resolving NDS UNC names, we
+ // need to re-point the irpcontext at the correct SCB.
+ // We can't hold the RCB or the open lock while we do
+ // this!
+ //
+ // It is ok to release the open lock since we know
+ // that we have an already created VCB and that we're
+ // not creating a new vcb.
+ //
+
+ if ( Vcb->Scb != IrpContext->pScb ) {
+
+ NwReferenceScb( Vcb->Scb->pNpScb );
+
+ NwReleaseOpenLock( );
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwDereferenceScb( IrpContext->pNpScb );
+
+ IrpContext->pScb = Vcb->Scb;
+ IrpContext->pNpScb = Vcb->Scb->pNpScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireOpenLock( );
+
+ }
+
+ } else if ( !FindExisting ) {
+
+ //
+ // Can't hold the RCB while creating a new VCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ Vcb = NwCreateVcb(
+ IrpContext,
+ IrpContext->pScb,
+ &UidVolumeName,
+ ShareType,
+ DriveLetter,
+ ExplicitConnection );
+
+ if ( Vcb ) {
+ DebugTrace(0, Dbg, "Created new VCB = %08lx\n", Vcb);
+ }
+
+ } else {
+
+ //
+ // If we didn't find anything and don't want
+ // to do a create, make sure the caller doesn't
+ // try to process the nds path.
+ //
+
+ IrpContext->Specific.Create.NeedNdsData = FALSE;
+ }
+
+ } finally {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if (UidVolumeName.Buffer != NULL) {
+ FREE_POOL( UidVolumeName.Buffer );
+ }
+ }
+
+ DebugTrace(-1, Dbg, "NwFindVcb\n", 0);
+ return( Vcb );
+
+}
+
+PVCB
+NwCreateVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initialize a new VCB. The
+ workstation tries to connect to the Volume. If successful
+ it creates a VCB and it is inserted into the volume
+ prefix table.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information.
+
+ Scb - A pointer to the SCB for this volume.
+
+ VolumeName - The name of the volume to create.
+
+ ShareType - The type of share to create.
+
+ DriveLetter - The drive letter assigned to this volume, or 0 if none.
+
+ ExplicitConnection - TRUE if we are creating this VCB due to an
+ add connection request. FALSE if we are creating the VCB to
+ service a UNC request.
+
+Return Value:
+
+ VCB - A pointer to the newly created DCB.
+ NULL - Could not create a DCB, or failed to connect to the volume.
+
+--*/
+
+{
+ PVCB Vcb;
+ PWCH VolumeNameBuffer;
+ PWCH ShareNameBuffer;
+ PWCH ConnectNameBuffer;
+ UCHAR DirectoryHandle;
+ ULONG QueueId;
+ BYTE *pbQueue, *pbRQueue;
+ BOOLEAN PrintQueue = FALSE;
+ NTSTATUS Status;
+ CHAR LongNameSpace = LFN_NO_OS2_NAME_SPACE;
+ CHAR VolumeNumber = -1;
+ CHAR DriveNumber = 0;
+ USHORT PreludeLength, ConnectNameLength;
+ PNONPAGED_SCB NpScb = Scb->pNpScb;
+
+ UNICODE_STRING ShareName;
+ UNICODE_STRING LongShareName;
+ PWCH p;
+
+ BOOLEAN InsertedColon;
+ BOOLEAN LongName = FALSE;
+ BOOLEAN LicensedConnection = FALSE;
+
+ PUNICODE_STRING puConnectName;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCreateVcb\n", 0);
+ DebugTrace( 0, Dbg, " ->Server = %wZ\n", &NpScb->ServerName );
+ DebugTrace( 0, Dbg, " ->VolumeName = %wZ\n", VolumeName );
+ DebugTrace( 0, Dbg, " ->DriveLetter = %x\n", DriveLetter );
+
+ Vcb = NULL;
+ ShareName.Buffer = NULL;
+
+ if ( IrpContext != NULL &&
+ IrpContext->Specific.Create.NdsCreate ) {
+
+ //
+ // If we don't have the NDS data for this create, bail out
+ // and have the create thread get the data before re-attempting
+ // the create. This is kind of weird, but we have to do it
+ // so that we handle the open lock correctly and prevent
+ // duplicate creates.
+ //
+
+ if ( IrpContext->Specific.Create.NeedNdsData ) {
+ DebugTrace( -1, Dbg, "NwCreateVcb: Need NDS data to continue.\n", 0 );
+ return NULL;
+ }
+
+ ConnectNameLength = IrpContext->Specific.Create.UidConnectName.Length;
+ puConnectName = &IrpContext->Specific.Create.UidConnectName;
+
+ } else {
+
+ puConnectName = VolumeName;
+ ConnectNameLength = 0;
+ }
+
+ DebugTrace( 0, Dbg, " ->ConnectName = %wZ\n", puConnectName );
+
+ if ( IrpContext != NULL) {
+
+ //
+ // Build the share name from the volume name.
+ //
+ // The share name will either be 'volume:' or 'volume:path\path'
+ //
+
+ //
+ // Allocate space for the share name buffer, and copy the volume
+ // name to the share name buffer, skipping the server name and
+ // the leading backslash.
+ //
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+
+ if ( ShareType == RESOURCETYPE_PRINT ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ } else if ( ShareType == RESOURCETYPE_ANY) {
+ ShareType = RESOURCETYPE_DISK;
+ }
+
+ PreludeLength = Scb->UidServerName.Length +
+ sizeof( L"X:") + sizeof(WCHAR);
+
+ } else if ( DriveLetter >= L'1' && DriveLetter <= L'9' ) {
+
+ if ( ShareType == RESOURCETYPE_DISK ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ } else if ( ShareType == RESOURCETYPE_ANY) {
+ ShareType = RESOURCETYPE_PRINT;
+ }
+
+ PreludeLength = Scb->UidServerName.Length +
+ sizeof( L"LPTX") + sizeof(WCHAR);
+
+ } else {
+ PreludeLength = Scb->UidServerName.Length + sizeof(WCHAR);
+ }
+
+ //
+ // Quick check for bogus volume name.
+ //
+
+ if ( puConnectName->Length <= PreludeLength ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ }
+
+ //
+ // Clip the NDS share name at the appropriate spot.
+ //
+
+ if ( IrpContext->Specific.Create.NdsCreate ) {
+ ShareName.Length = (USHORT)IrpContext->Specific.Create.dwNdsShareLength;
+ } else {
+ ShareName.Length = puConnectName->Length - PreludeLength;
+ }
+
+ ShareName.Buffer = ALLOCATE_POOL_EX( PagedPool, ShareName.Length + sizeof(WCHAR) );
+
+ RtlMoveMemory(
+ ShareName.Buffer,
+ puConnectName->Buffer + PreludeLength / sizeof(WCHAR),
+ ShareName.Length );
+
+ ShareName.MaximumLength = ShareName.Length;
+
+ DebugTrace( 0, Dbg, " ->ServerShare = %wZ\n", &ShareName );
+
+ //
+ // Create a long share name.
+ //
+
+ LongShareName.Length = ShareName.Length;
+ LongShareName.Buffer = puConnectName->Buffer + PreludeLength / sizeof(WCHAR);
+
+ //
+ // Now scan the share name for the 1st slash.
+ //
+
+ InsertedColon = FALSE;
+
+ for ( p = ShareName.Buffer; p < ShareName.Buffer + ShareName.Length/sizeof(WCHAR); p++ ) {
+ if ( *p == L'\\') {
+ *p = L':';
+ InsertedColon = TRUE;
+ break;
+ }
+ }
+
+ if ( !InsertedColon ) {
+
+ //
+ // We need to append a column to generate the share name.
+ // Since we already allocated an extra WCHAR of buffer space,
+ // just append the ':' to the share name.
+ //
+
+ ShareName.Buffer[ShareName.Length / sizeof(WCHAR)] = L':';
+ ShareName.Length += 2;
+ }
+
+ ASSERT( ShareType == RESOURCETYPE_ANY ||
+ ShareType == RESOURCETYPE_DISK ||
+ ShareType == RESOURCETYPE_PRINT );
+
+ //
+ // If there are no vcb's and no nds streams connected to this scb and
+ // this is a Netware 4.x server that is NDS authenticated, then we
+ // haven't yet licensed this connection and we should do so.
+ //
+
+ if ( ( IrpContext->pScb->MajorVersion > 3 ) &&
+ ( IrpContext->pScb->UserName.Length == 0 ) &&
+ ( IrpContext->pScb->VcbCount == 0 ) &&
+ ( IrpContext->pScb->OpenNdsStreams == 0 ) ) {
+
+ Status = NdsLicenseConnection( IrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ ExRaiseStatus( STATUS_REMOTE_SESSION_LIMIT );
+ }
+
+ LicensedConnection = TRUE;
+ }
+
+ if ( ShareType == RESOURCETYPE_ANY ||
+ ShareType == RESOURCETYPE_DISK ) {
+
+ GetLongNameSpaceForVolume(
+ IrpContext,
+ ShareName,
+ &LongNameSpace,
+ &VolumeNumber );
+
+ //
+ // BUGBUG: If this is the deref of a directory map, the path we have
+ // been provided is the short name space path. We have to get the
+ // long name path to connect up the long name space for the user!
+ //
+
+ if ( ( IrpContext->Specific.Create.NdsCreate ) &&
+ ( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_DIRMAP ) ) {
+ LongNameSpace = LFN_NO_OS2_NAME_SPACE;
+ }
+
+ //
+ // Try to get a permanent handle to the volume.
+ //
+
+ if ( LongNameSpace == LFN_NO_OS2_NAME_SPACE ) {
+
+ DriveNumber = GetNewDriveNumber(Scb);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_DIR_HANDLE,
+ 0,
+ DriveNumber,
+ &ShareName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &DirectoryHandle );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FreeDriveNumber( Scb, DriveNumber );
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbC",
+ NCP_LFN_ALLOCATE_DIR_HANDLE,
+ LongNameSpace,
+ 0,
+ 0, // Mode = permanent
+ VolumeNumber,
+ LFN_FLAG_SHORT_DIRECTORY,
+ 0xFF, // Flag
+ &LongShareName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &DirectoryHandle );
+ }
+
+ //
+ // WARNING. See comment towards end of NwCreateFcb() !!!
+ //
+ if ( FavourLongNames || !IsFatNameValid( &LongShareName ) ) {
+ LongName = TRUE;
+ }
+ }
+
+ if ( ( Status == STATUS_NO_SUCH_DEVICE ) &&
+ ( ShareType != RESOURCETYPE_ANY ) ) {
+
+ //
+ // Asked for disk and it failed. If its ANY, then try print.
+ //
+
+ if (DriveNumber) {
+ FreeDriveNumber( Scb, DriveNumber );
+ }
+
+ FREE_POOL( ShareName.Buffer );
+
+ if ( LicensedConnection ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_NAME );
+ return( NULL );
+ }
+
+ }
+
+ if ( ShareType == RESOURCETYPE_PRINT ||
+ ( ShareType == RESOURCETYPE_ANY && !NT_SUCCESS( Status ) ) ) {
+
+ //
+ // Try to connect to a print queue. If this is a bindery
+ // server or an nds server with bindery emulation, we scan
+ // the bindery for the QueueId. Otherwise, the QueueId is
+ // simply the ds object id with the byte ordering reversed.
+ //
+
+ ShareName.Length -= sizeof(WCHAR);
+
+ if ( ( Scb->MajorVersion < 4 ) ||
+ ( !( IrpContext->Specific.Create.NdsCreate ) ) ) {
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "SdwJ", // Format string
+ NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
+ -1, // Previous ID
+ OT_PRINT_QUEUE,
+ &ShareName ); // Queue Name
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &QueueId );
+ }
+
+ } else {
+
+ if ( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ DebugTrace( 0, Dbg, "Mapping NDS print queue %08lx\n",
+ IrpContext->Specific.Create.dwNdsOid );
+
+ pbQueue = (BYTE *)&IrpContext->Specific.Create.dwNdsOid;
+ pbRQueue = (BYTE *)&QueueId;
+
+ pbRQueue[0] = pbQueue[3];
+ pbRQueue[1] = pbQueue[2];
+ pbRQueue[2] = pbQueue[1];
+ pbRQueue[3] = pbQueue[0];
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Nds object is not a print queue.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ PrintQueue = TRUE;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if (DriveNumber) {
+ FreeDriveNumber( Scb, DriveNumber );
+ }
+
+ FREE_POOL( ShareName.Buffer );
+
+ if ( LicensedConnection ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ return( NULL );
+ }
+
+ } else {
+ DirectoryHandle = 1;
+ }
+
+ //
+ // Allocate and initialize structures.
+ //
+
+ try {
+
+ Vcb = ALLOCATE_POOL_EX( PagedPool, sizeof( VCB ) + // vcb
+ VolumeName->Length + // volume name
+ ShareName.Length + // share name
+ ConnectNameLength ); // connect name
+
+ RtlZeroMemory( Vcb, sizeof( VCB ) );
+ Vcb->NodeTypeCode = NW_NTC_VCB;
+ Vcb->NodeByteSize = sizeof( VCB ) +
+ VolumeName->Length +
+ ShareName.Length +
+ ConnectNameLength;
+
+ InitializeListHead( &Vcb->FcbList );
+
+ VolumeNameBuffer = (PWCH)(Vcb + 1);
+ ShareNameBuffer = (PWCH)((PCHAR)VolumeNameBuffer + VolumeName->Length);
+ ConnectNameBuffer = (PWCH)((PCHAR)ShareNameBuffer + ShareName.Length);
+
+ Vcb->Reference = 1;
+
+ //
+ // Copy the volume name
+ //
+
+ RtlCopyMemory( VolumeNameBuffer, VolumeName->Buffer, VolumeName->Length );
+ Vcb->Name.MaximumLength = VolumeName->Length;
+ Vcb->Name.Length = VolumeName->Length;
+ Vcb->Name.Buffer = VolumeNameBuffer;
+
+ //
+ // Copy the share name
+ //
+
+ if ( IrpContext != NULL) {
+
+ RtlCopyMemory( ShareNameBuffer, ShareName.Buffer, ShareName.Length );
+ Vcb->ShareName.MaximumLength = ShareName.Length;
+ Vcb->ShareName.Length = ShareName.Length;
+ Vcb->ShareName.Buffer = ShareNameBuffer;
+
+ }
+
+ //
+ // Copy the connect name
+ //
+
+ if ( ConnectNameLength ) {
+
+ RtlCopyMemory( ConnectNameBuffer,
+ IrpContext->Specific.Create.UidConnectName.Buffer,
+ IrpContext->Specific.Create.UidConnectName.Length );
+ Vcb->ConnectName.MaximumLength = IrpContext->Specific.Create.UidConnectName.Length;
+ Vcb->ConnectName.Length = IrpContext->Specific.Create.UidConnectName.Length;
+ Vcb->ConnectName.Buffer = ConnectNameBuffer;
+
+ }
+
+ if ( ExplicitConnection ) {
+
+ //
+ // Bump the reference count to account for this drive being
+ // mapped via an explicit connection.
+ //
+
+ NwReferenceVcb( Vcb );
+ SetFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ SetFlag( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY );
+
+ }
+
+ if ( LongName ) {
+ SetFlag( Vcb->Flags, VCB_FLAG_LONG_NAME );
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if ( DriveLetter != 0) {
+
+ //
+ // Insert this VCB in the drive map table.
+ //
+
+ if ( DriveLetter >= 'A' && DriveLetter <= 'Z' ) {
+ DriveMapTable[DriveLetter - 'A'] = Vcb;
+ } else {
+ DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - '1'] = Vcb;
+ }
+
+ Vcb->DriveLetter = DriveLetter;
+
+ } else {
+
+ //
+ // Insert this VCB in the prefix table.
+ //
+
+ RtlInsertUnicodePrefix(
+ &NwRcb.VolumeNameTable,
+ &Vcb->Name,
+ &Vcb->PrefixEntry );
+ }
+
+ //
+ // Add this VCB to the global list.
+ //
+
+ InsertTailList( &GlobalVcbList, &Vcb->GlobalVcbListEntry );
+ Vcb->SequenceNumber = CurrentVcbEntry++;
+
+ //
+ // Insert this VCB in the per SCB list
+ //
+
+ Vcb->Scb = Scb;
+ InsertTailList( &Scb->ScbSpecificVcbQueue, &Vcb->VcbListEntry );
+ ++Scb->VcbCount;
+ NwReferenceScb( Scb->pNpScb );
+
+ if ( ExplicitConnection ) {
+
+ //
+ // Count this as an open file on the SCB.
+ //
+
+ ++Vcb->Scb->OpenFileCount;
+ }
+
+ if ( !PrintQueue) {
+
+ PLIST_ENTRY VcbQueueEntry;
+ PVCB pVcb;
+
+ Vcb->Specific.Disk.Handle = DirectoryHandle;
+ Vcb->Specific.Disk.LongNameSpace = LongNameSpace;
+ Vcb->Specific.Disk.VolumeNumber = VolumeNumber;
+ Vcb->Specific.Disk.DriveNumber = DriveNumber;
+
+ //
+ // Appears that some servers can reuse the same permanent drive handle.
+ // if this happens we want to make the old handle invalid otherwise
+ // we will keep on using the new volume as if its the old one.
+ //
+
+ for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &Scb->ScbSpecificVcbQueue;
+ VcbQueueEntry = pVcb->VcbListEntry.Flink ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ if (( pVcb->Specific.Disk.Handle == DirectoryHandle ) &&
+ ( pVcb->Specific.Disk.VolumeNumber != VolumeNumber )) {
+ // Invalidate the old handle
+ pVcb->Specific.Disk.Handle = (CHAR)-1;
+
+ // We could assume that the new one is correct but I don't think we will....
+ Vcb->Specific.Disk.Handle = (CHAR)-1;
+ break;
+ }
+ }
+ }
+
+ } else {
+ SetFlag( Vcb->Flags, VCB_FLAG_PRINT_QUEUE );
+ Vcb->Specific.Print.QueueId = QueueId;
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ } finally {
+
+ if ( AbnormalTermination() ) {
+
+ if ( Vcb != NULL ) FREE_POOL( Vcb );
+
+ if ( LicensedConnection ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+ }
+
+ if ( ShareName.Buffer != NULL ) {
+ FREE_POOL( ShareName.Buffer );
+ }
+
+ DebugTrace(-1, Dbg, "NwCreateVcb %lx\n", Vcb);
+ }
+
+ return( Vcb );
+}
+
+VOID
+NwReopenVcbHandlesForScb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reopens VCB handles after the autoreconnects to a server.
+
+ *** This IrpContext must already be at the head of the SCB queue.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information.
+
+ Scb - A pointer to the SCB for this volume.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY VcbQueueEntry, NextVcbQueueEntry;
+ PVCB pVcb;
+
+ PLIST_ENTRY FcbQueueEntry;
+ PLIST_ENTRY IcbQueueEntry;
+ PFCB pFcb;
+ PICB pIcb;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Walk the list of VCBs for this SCB
+ //
+
+ for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &Scb->ScbSpecificVcbQueue;
+ VcbQueueEntry = NextVcbQueueEntry ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( pVcb->Specific.Disk.Handle != 1 ) {
+
+ //
+ // Skip reconnecting SYS:LOGIN, since we get it for free.
+ //
+
+ //
+ // Reference the VCB so it can't disappear on us, then release
+ // the RCB.
+ //
+
+ NwReferenceVcb( pVcb );
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // Try to get a permanent handle to the volume.
+ //
+
+ if ( BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "SdwU", // Format string
+ NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
+ -1, // Previous ID
+ OT_PRINT_QUEUE,
+ &pVcb->ShareName ); // Queue Name
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &pVcb->Specific.Print.QueueId );
+ }
+
+ } else {
+
+ NwReopenVcbHandle( IrpContext, pVcb);
+
+ }
+
+
+ //
+ // Setup for the next loop iteration.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Walk the list of DCSs for this VCB and make them all valid.
+ //
+
+ for ( FcbQueueEntry = pVcb->FcbList.Flink;
+ FcbQueueEntry != &pVcb->FcbList;
+ FcbQueueEntry = FcbQueueEntry->Flink ) {
+
+ pFcb = CONTAINING_RECORD( FcbQueueEntry, FCB, FcbListEntry );
+
+ if ( pFcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ //
+ // Walk the list of ICBs for this FCB or DCB
+ //
+
+ for ( IcbQueueEntry = pFcb->IcbList.Flink;
+ IcbQueueEntry != &pFcb->IcbList;
+ IcbQueueEntry = IcbQueueEntry->Flink ) {
+
+ pIcb = CONTAINING_RECORD( IcbQueueEntry, ICB, ListEntry );
+
+ //
+ // Mark the ICB handle invalid.
+ //
+
+ pIcb->State = ICB_STATE_OPENED;
+ }
+ }
+ }
+
+ }
+
+ NextVcbQueueEntry = VcbQueueEntry->Flink;
+
+ if ( pVcb->Specific.Disk.Handle != 1 ) {
+ NwDereferenceVcb( pVcb, NULL, TRUE );
+ }
+
+ }
+
+ NwReleaseRcb( &NwRcb );
+}
+
+VOID
+NwReopenVcbHandle(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reopens a VCB handle after it appears that the server
+ may have dismounted and remounted the volume.
+
+ *** This IrpContext must already be at the head of the SCB queue.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information.
+
+ Vcb - A pointer to the VCB for this volume.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ ASSERT( Vcb->Scb->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ if ( Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ) {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_DIR_HANDLE,
+ 0,
+ Vcb->Specific.Disk.DriveNumber,
+ &Vcb->ShareName );
+
+ } else {
+ UNICODE_STRING Name;
+
+ PWCH thisChar, lastChar;
+
+ Status = DuplicateUnicodeStringWithString (
+ &Name,
+ &Vcb->ShareName,
+ PagedPool);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ // Not much we can do now.
+ return;
+ }
+
+ thisChar = Name.Buffer;
+ lastChar = &Name.Buffer[ Name.Length / sizeof(WCHAR) ];
+
+ //
+ // Change the : to a backslash so that FormatMessage works
+ //
+
+ while ( thisChar < lastChar ) {
+ if (*thisChar == L':' ) {
+ *thisChar = L'\\';
+ break;
+ }
+ thisChar++;
+ }
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbC",
+ NCP_LFN_ALLOCATE_DIR_HANDLE,
+ Vcb->Specific.Disk.LongNameSpace,
+ 0,
+ 0, // Mode = permanent
+ Vcb->Specific.Disk.VolumeNumber,
+ LFN_FLAG_SHORT_DIRECTORY,
+ 0xFF, // Flag
+ &Name );
+
+ if ( Name.Buffer != NULL ) {
+ FREE_POOL( Name.Buffer );
+ }
+
+ }
+
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Vcb->Specific.Disk.Handle );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Vcb->Specific.Disk.Handle = (CHAR)-1;
+ } else {
+
+ PLIST_ENTRY VcbQueueEntry;
+ PVCB pVcb;
+
+ //
+ // Appears that some servers can reuse the same permanent drive handle.
+ // if this happens we want to make the old handle invalid otherwise
+ // we will keep on using the new volume as if its the old one.
+ //
+ // Note that we reach the scb pointer from the npscb pointer because
+ // the scb pointer isn't always valid. These few cases where only one
+ // pointer is set should be found and fixed.
+ //
+
+ for ( VcbQueueEntry = IrpContext->pNpScb->pScb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &IrpContext->pNpScb->pScb->ScbSpecificVcbQueue;
+ VcbQueueEntry = pVcb->VcbListEntry.Flink ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ if (( pVcb->Specific.Disk.Handle == Vcb->Specific.Disk.Handle ) &&
+ ( pVcb->Specific.Disk.VolumeNumber != Vcb->Specific.Disk.VolumeNumber )) {
+ // Invalidate the old handle
+ pVcb->Specific.Disk.Handle = (CHAR)-1;
+
+ // We could assume that the new one is correct but I don't think we will....
+ Vcb->Specific.Disk.Handle = (CHAR)-1;
+ break;
+ }
+ }
+ }
+ }
+
+}
+#ifdef NWDBG
+
+VOID
+NwReferenceVcb (
+ IN PVCB Vcb
+ )
+/*++
+
+Routine Description:
+
+ This routine increments the FCB count for a VCB.
+
+Arguments:
+
+ VCB - A pointer to an VCB.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwReferenceVcb %08lx\n", Vcb);
+ DebugTrace(0, Dbg, "Current Reference count = %d\n", Vcb->Reference );
+
+ ASSERT( NodeType( Vcb ) == NW_NTC_VCB );
+
+ ++Vcb->Reference;
+
+}
+#endif
+
+
+VOID
+NwDereferenceVcb (
+ IN PVCB Vcb,
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN BOOLEAN OwnRcb
+ )
+/*++
+
+Routine Description:
+
+ This routine decrement the FCB count for a VCB.
+ If the count goes to zero, we record the time. The scavenger
+ thread will cleanup delete the VCB if it remains idle.
+
+ This routine may be called with the RCB owned and the irpcontext
+ at the head of the queue. Be careful when dequeueing the irp
+ context or acquiring any resources!
+
+Arguments:
+
+ VCB - A pointer to an VCB.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSCB Scb = Vcb->Scb;
+ PNONPAGED_SCB pOrigNpScb = NULL;
+
+#ifdef NWDBG
+ BOOLEAN OwnRcbExclusive = FALSE;
+#endif
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDereferenceVcb %08lx\n", Vcb);
+
+ ASSERT( NodeType( Vcb ) == NW_NTC_VCB );
+
+#ifdef NWDBG
+
+ //
+ // A little extra lock checking.
+ //
+
+ OwnRcbExclusive = ExIsResourceAcquiredExclusiveLite( &(NwRcb.Resource) );
+
+ if ( OwnRcb ) {
+ ASSERT( OwnRcbExclusive );
+ } else {
+ ASSERT( !OwnRcbExclusive );
+ }
+
+#endif
+
+ //
+ // We have to get to the right scb queue before doing this
+ // so that CleanupVcb unlicenses the correct connection.
+ //
+
+ if ( ( IrpContext ) &&
+ ( IrpContext->pNpScb->pScb->MajorVersion > 3 ) &&
+ ( IrpContext->pNpScb != Scb->pNpScb ) ) {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ pOrigNpScb = IrpContext->pNpScb;
+ ASSERT( pOrigNpScb != NULL );
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ IrpContext->pScb = Scb;
+ IrpContext->pNpScb = Scb->pNpScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // If the caller owned the RCB, we have to make sure
+ // we re-acquire the RCB reference that we freed for
+ // them so that they don't lose access to the resource
+ // too early.
+ //
+
+ if ( OwnRcb ) {
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ }
+
+ }
+
+ //
+ // Acquire the lock to protect the Reference count.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ DebugTrace(0, Dbg, "Current Reference count = %d\n", Vcb->Reference );
+ --Vcb->Reference;
+
+ if ( Vcb->Reference == 0 ) {
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY ) ||
+ IrpContext == NULL ) {
+
+ //
+ // Either this is a UNC path, or we don't have an IRP context
+ // to do the VCB cleanup. Simply timestamp the VCB and the
+ // scavenger will cleanup if the VCB remains idle.
+ //
+
+ KeQuerySystemTime( &Vcb->LastUsedTime );
+ NwReleaseRcb( &NwRcb );
+
+ } else {
+
+ //
+ // This VCB is being explicitly deleted by the user.
+ // Make it go away now. This will release the RCB.
+ //
+
+ NwCleanupVcb( Vcb, IrpContext );
+
+ }
+
+ } else {
+
+ NwReleaseRcb( &NwRcb );
+ }
+
+ //
+ // At this point, we've released our acquisition of the RCB, but
+ // the caller may still own the RCB. To prevent a deadlock, we
+ // have to be careful when we put this irpcontext back on the
+ // original server.
+ //
+
+ if ( pOrigNpScb ) {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ IrpContext->pNpScb = pOrigNpScb;
+ IrpContext->pScb = pOrigNpScb->pScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Re-acquire for the caller.
+ //
+
+ if ( OwnRcb ) {
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "NwDereferenceVcb\n", 0);
+
+}
+
+
+VOID
+NwCleanupVcb(
+ IN PVCB pVcb,
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up and frees a VCB.
+
+ This routine must be called with the RCB held to
+ protect the drive map tables and unicode prefix
+ tables. The caller must own the IRP context at
+ the head of the SCB queue. This routine will
+ free the RCB and dequeue the irp context.
+
+Arguments:
+
+ pVcb - A pointer to the VCB to free.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ CHAR Handle;
+ BOOLEAN CallDeleteScb = FALSE;
+ PSCB pScb = pVcb->Scb;
+ PNONPAGED_SCB pNpScb = pScb->pNpScb;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCleanupVcb...\n", 0);
+
+ ASSERT( pVcb->NodeTypeCode == NW_NTC_VCB );
+ ASSERT( IsListEmpty( &pVcb->FcbList ) );
+ ASSERT( pVcb->OpenFileCount == 0 );
+
+ DebugTrace(0, Dbg, "Cleaning Vcb %08lx\n", pVcb);
+
+ //
+ // Remove the VCB from the drive map table. The RCB is owned, so
+ // the drive map table and vcb lists are protected.
+ //
+
+ if ( pVcb->DriveLetter != 0 ) {
+ if ( pVcb->DriveLetter >= L'A' && pVcb->DriveLetter <= L'Z' ) {
+ DriveMapTable[pVcb->DriveLetter - L'A'] = NULL;
+ } else {
+ DriveMapTable[MAX_DISK_REDIRECTIONS + pVcb->DriveLetter - L'1'] = NULL;
+ }
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+ FreeDriveNumber( pVcb->Scb, pVcb->Specific.Disk.DriveNumber );
+ }
+ }
+
+ //
+ // Remove the VCB from the Volume Name table.
+ //
+
+ RtlRemoveUnicodePrefix ( &NwRcb.VolumeNameTable, &pVcb->PrefixEntry );
+
+ //
+ // Remove the VCB from the global list
+ //
+
+ RemoveEntryList( &pVcb->GlobalVcbListEntry );
+
+ //
+ // Remove the VCB from our SCB's VCB list.
+ //
+
+ RemoveEntryList( &pVcb->VcbListEntry );
+
+ --pScb->VcbCount;
+
+ //
+ // There is no server jumping allowed!! We should have
+ // pre-located the correct server to avoid deadlock problems.
+ //
+
+ ASSERT( IrpContext->pNpScb == pNpScb );
+
+ //
+ // If we are cleaning up the last vcb on an NDS server and
+ // there are no open streams, we can unlicense the connection.
+ //
+
+ if ( ( pScb->MajorVersion > 3 ) &&
+ ( pScb->UserName.Length == 0 ) &&
+ ( pScb->VcbCount == 0 ) &&
+ ( pScb->OpenNdsStreams == 0 ) ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ //
+ // If this is a VCB for a share, remove the volume handle.
+ //
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ Handle = pVcb->Specific.Disk.Handle;
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
+ Handle );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+ }
+
+ //
+ // We can now free the VCB memory.
+ //
+
+ FREE_POOL( pVcb );
+
+ //
+ // If there are no handles open (and hence no explicit connections)
+ // and this is a bindery login, then we should logout and disconnect
+ // from this server. This is most important when a user has a
+ // login count on a server set to 1 and wants to access the server
+ // from another machine.
+ //
+ // Release the RCB in case we get off the head of the queue in
+ // NwLogoffAndDisconnect.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ if ( ( pScb->IcbCount == 0 ) &&
+ ( pScb->OpenFileCount == 0 ) &&
+ ( pNpScb->State == SCB_STATE_IN_USE ) &&
+ ( pScb->UserName.Length != 0 ) ) {
+
+ NwLogoffAndDisconnect( IrpContext, pNpScb );
+ }
+
+ //
+ // We might need to restore the server pointers.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwDereferenceScb( pScb->pNpScb );
+
+ DebugTrace(-1, Dbg, "NwCleanupVcb exit\n", 0);
+ return;
+}
+
+VOID
+NwCloseAllVcbs(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine sends closes all open VCB handles.
+
+Arguments:
+
+ pIrpContext - The IRP context for this request.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry, NextScbQueueEntry;
+ PLIST_ENTRY VcbQueueEntry, NextVcbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ PVCB pVcb;
+ BOOLEAN VcbDeleted;
+
+ PAGED_CODE();
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ pScb = pNpScb->pScb;
+ if ( pScb == NULL ) {
+ continue;
+ }
+
+ NwReferenceScb( pNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Get to the head of the SCB queue so that we don't deadlock
+ // if we need to send packets in NwCleanupVcb().
+ //
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pNpScb->pScb;
+
+ NwAppendToQueueAndWait( pIrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // NwCleanupVcb releases the RCB, but we can't be guaranteed
+ // the state of the Vcb list when we release the RCB.
+ //
+ // If we need to cleanup a VCB, release the lock, and start
+ // processing the list again.
+ //
+
+ VcbDeleted = TRUE;
+
+ while ( VcbDeleted ) {
+
+ VcbDeleted = FALSE;
+
+ //
+ // Walk the list of VCBs for this SCB
+ //
+
+ for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &pScb->ScbSpecificVcbQueue;
+ VcbQueueEntry = NextVcbQueueEntry ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+ NextVcbQueueEntry = VcbQueueEntry->Flink;
+
+ //
+ // If this VCB is mapped to a drive letter, delete the mapping
+ // now.
+ //
+
+ if ( BooleanFlagOn( pVcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) {
+
+ //
+ // Remove the VCB from the global list.
+ //
+
+ ClearFlag( pVcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ --pVcb->Reference;
+ --pVcb->Scb->OpenFileCount;
+ }
+
+ if ( pVcb->DriveLetter >= L'A' && pVcb->DriveLetter <= L'Z' ) {
+ DriveMapTable[ pVcb->DriveLetter - 'A' ] = NULL;
+ } else if ( pVcb->DriveLetter >= L'1' && pVcb->DriveLetter <= L'9' ) {
+ DriveMapTable[ MAX_DISK_REDIRECTIONS + pVcb->DriveLetter - '1' ] = NULL;
+ } else {
+ ASSERT( pVcb->DriveLetter == 0 );
+ }
+
+ if ( pVcb->Reference == 0 ) {
+
+ NwCleanupVcb( pVcb, pIrpContext );
+
+ //
+ // Get back to the head of the queue.
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ VcbDeleted = TRUE;
+ break;
+
+ } else {
+ SetFlag( pVcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY );
+ }
+
+ }
+ }
+
+ //
+ // Get off the head of this SCB and move on.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NwDequeueIrpContext( pIrpContext, TRUE );
+ NwReleaseRcb( &NwRcb );
+ NwDereferenceScb( pNpScb );
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+}
+
+BOOLEAN
+GetLongNameSpaceForVolume(
+ IN PIRP_CONTEXT IrpContext,
+ IN UNICODE_STRING ShareName,
+ OUT PCHAR VolumeLongNameSpace,
+ OUT PCHAR VolumeNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine determines the name space index for long name support.
+ This is accomplished by looking for the OS2 name space.
+
+Arguments:
+
+ pIrpContext - The IRP context for this request.
+
+ ShareName - The name of the interesting volume.
+
+ VolumeLongNameSpace - Returns the name space id of the OS/2 name space.
+
+ VolumeNumber - Returns the volume number.
+
+Return Value:
+
+ TRUE - The volume support long names.
+ FALSE - The volume does not support long names.
+
+--*/
+{
+ NTSTATUS Status;
+ char *ptr;
+ int i;
+ char length;
+ BOOLEAN LongNameSpace;
+ CHAR NumberOfNameSpaces, NumberOfInfoRecords;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetLongNameSpaceForVolume...\n", 0);
+
+ *VolumeLongNameSpace = LFN_NO_OS2_NAME_SPACE;
+
+ //
+ // Get the ordinal number of this volume.
+ //
+
+ for ( i = 0; ShareName.Buffer[i] != ':'; i++);
+ ShareName.Length = i * sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, "Volume name %wZ\n", &ShareName );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SU",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_NUMBER,
+ &ShareName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ VolumeNumber );
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ DebugTrace( 0, Dbg, "Couldn't get volume number\n", 0);
+ DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> -1\n", 0);
+ return( FALSE );
+ }
+
+ //
+ // Send a get name space info request, and wait for the response.
+ //
+
+ DebugTrace( 0, Dbg, "Querying volume number %d\n", *VolumeNumber );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_NAME_SPACE_INFO,
+ *VolumeNumber );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &NumberOfNameSpaces );
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ DebugTrace( 0, Dbg, "Couldn't get name space info\n", 0);
+ DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> -1\n", 0);
+ return( FALSE );
+ }
+
+ //
+ // Parse the response, it has the following format:
+ //
+ // NCP Header
+ //
+ // Number of Name Space Records (n1, byte)
+ //
+ // n1 Name Space Records
+ // Length (l1, byte)
+ // Value (l1 bytes, non-NUL-terminated ASCII string)
+ //
+ // Number of Name Space Info Records (n2, byte)
+ //
+ // n2 Name Space Info Records
+ // Record number (byte)
+ // Length (l2, byte)
+ // Value (l2 bytes, non-NUL-terminated ASCII string)
+ //
+ // Loaded name spaces (n3, byte)
+ // Loaded name space list (n3 bytes, each byte refers to the ordinal
+ // number of a name space record )
+ //
+ // Volume name spaces (n3, byte)
+ // Volume name space list (n3 bytes, as above)
+ //
+ // Volume Data Streams (n3, byte)
+ // Volume Data Streams (n3 bytes, each byte refers to the ordinal
+ // number of a name space info record )
+ //
+
+ DebugTrace( 0, Dbg, "Number of name spaces = %d\n", NumberOfNameSpaces );
+
+ ptr = &IrpContext->rsp[ 9 ];
+ LongNameSpace = FALSE;
+
+ //
+ // Skip the loaded name space list.
+ //
+
+ for ( i = 0 ; i < NumberOfNameSpaces ; i++ ) {
+ length = *ptr++;
+ ptr += length;
+ }
+
+ //
+ // Skip the supported data streams list.
+ //
+
+ NumberOfInfoRecords = *ptr++;
+
+ for ( i = 0 ; i < NumberOfInfoRecords ; i++ ) {
+ ptr++; // Skip record number
+ length = *ptr;
+ ptr += length + 1;
+ }
+
+ //
+ // Skip the supported data streams ordinal list.
+ //
+
+ length = *ptr;
+ ptr += length + 1;
+
+ //
+ // See if this volume supports long names.
+ //
+
+ length = *ptr++;
+ for ( i = 0; i < length ; i++ ) {
+ if ( *ptr++ == LONG_NAME_SPACE_ORDINAL ) {
+ LongNameSpace = TRUE;
+ *VolumeLongNameSpace = LONG_NAME_SPACE_ORDINAL;
+ }
+ }
+
+ if ( LongNameSpace ) {
+ DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> STATUS_SUCCESS\n", 0 );
+ } else {
+ DebugTrace(-1, Dbg, "No long name space for volume.\n", 0 );
+ }
+
+ return( LongNameSpace );
+}
+
+BOOLEAN
+IsFatNameValid (
+ IN PUNICODE_STRING FileName
+ )
+/*++
+
+Routine Description:
+
+ This routine checks if the specified file name is conformant to the
+ Fat 8.3 file naming rules.
+
+ FIXFIX Either get a wide version of FsRtlIsFatDbcsLegal or keep the OemString
+ FIXFIX version around for the packet we'll eventually create.
+
+Arguments:
+
+ FileName - Supplies the name to check.
+
+Return Value:
+
+ BOOLEAN - TRUE if the name is valid, FALSE otherwise.
+
+--*/
+
+{
+ STRING DbcsName;
+ int i;
+
+ PAGED_CODE();
+
+ //
+ // Build up the dbcs string to call the fsrtl routine to check
+ // for legal 8.3 formation
+ //
+
+ if (NT_SUCCESS(RtlUnicodeStringToCountedOemString( &DbcsName, FileName, TRUE))) {
+
+ for ( i = 0; i < DbcsName.Length; i++ ) {
+
+ if ( FsRtlIsLeadDbcsCharacter( DbcsName.Buffer[i] ) ) {
+
+ //
+ // Ignore lead bytes and trailing bytes
+ //
+
+ i++;
+
+ } else {
+
+ //
+ // disallow:
+ // '*' + 0x80 alt-170 (0xAA)
+ // '.' + 0x80 alt-174 (0xAE),
+ // '?' + 0x80 alt-191 (0xBF) the same as Dos clients.
+ //
+ // May need to add 229(0xE5) too.
+ //
+ // We also disallow spaces as valid FAT chars since
+ // NetWare treats them as part of the OS2 name space.
+ //
+
+ if ((DbcsName.Buffer[i] == 0xAA) ||
+ (DbcsName.Buffer[i] == 0xAE) ||
+ (DbcsName.Buffer[i] == 0xBF) ||
+ (DbcsName.Buffer[i] == ' ')) {
+
+ RtlFreeOemString( &DbcsName );
+ return FALSE;
+ }
+ }
+ }
+
+ if (FsRtlIsFatDbcsLegal( DbcsName, FALSE, TRUE, TRUE )) {
+
+ RtlFreeOemString( &DbcsName );
+
+ return TRUE;
+
+ }
+
+ RtlFreeOemString( &DbcsName );
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return FALSE;
+}
+
+CHAR
+GetNewDriveNumber (
+ IN PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ Portable NetWare needs us to give a different drive letter each time
+ we ask for a permanent handle. If we use the same one then:
+
+ net use s: \\port\sys
+ net use v: \\port\vol1
+ dir s:
+ <get contents of \\port\vol1 !!!!>
+
+
+Arguments:
+
+ Scb
+
+Return Value:
+
+ Letter assigned.
+
+--*/
+
+{
+
+ ULONG result = RtlFindClearBitsAndSet( &Scb->DriveMapHeader, 1, 0 );
+
+ PAGED_CODE();
+
+ if (result == 0xffffffff) {
+ return(0); // All used!
+ } else {
+ return('A' + (CHAR)(result & 0x00ff) );
+ }
+}
+
+VOID
+FreeDriveNumber(
+ IN PSCB Scb,
+ IN CHAR DriveNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine releases the appropriate Drivehandles bit.
+
+Arguments:
+
+ FileName - Supplies the name to check.
+
+Return Value:
+
+ BOOLEAN - TRUE if the name is valid, FALSE otherwise.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if (DriveNumber) {
+ RtlClearBits( &Scb->DriveMapHeader, (DriveNumber - 'A') & 0x00ff, 1);
+ }
+}
diff --git a/private/nw/rdr/struct.h b/private/nw/rdr/struct.h
new file mode 100644
index 000000000..6f6bdf9eb
--- /dev/null
+++ b/private/nw/rdr/struct.h
@@ -0,0 +1,1357 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Struct.h
+
+Abstract:
+
+ This module defines the data structures that make up the major internal
+ part of the NetWare file system.
+
+Author:
+
+ Colin Watson [ColinW] 18-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NWSTRUC_
+#define _NWSTRUC_
+
+#define byte UCHAR
+#define word USHORT
+#define dword ULONG
+
+typedef enum _PACKET_TYPE {
+ SAP_BROADCAST,
+ NCP_CONNECT,
+ NCP_FUNCTION,
+ NCP_SUBFUNCTION,
+ NCP_DISCONNECT,
+ NCP_BURST,
+ NCP_ECHO
+} PACKET_TYPE;
+
+typedef struct _NW_TDI_STRUCT {
+ HANDLE Handle;
+ PDEVICE_OBJECT pDeviceObject;
+ PFILE_OBJECT pFileObject;
+ USHORT Socket;
+} NW_TDI_STRUCT, *PNW_TDI_STRUCT;
+
+typedef
+NTSTATUS
+(*PEX) (
+ IN struct _IRP_CONTEXT* pIrpC,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+typedef
+VOID
+(*PRUN_ROUTINE) (
+ IN struct _IRP_CONTEXT *IrpContext
+ );
+
+typedef
+NTSTATUS
+(*PPOST_PROCESSOR) (
+ IN struct _IRP_CONTEXT *IrpContext
+ );
+
+typedef
+NTSTATUS
+(*PRECEIVE_ROUTINE) (
+ IN struct _IRP_CONTEXT *IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+//
+// The Scb (Server control Block) record corresponds to every server
+// connected to by the file system.
+// They are ordered in ScbQueue.
+// This structure is allocated from paged pool
+//
+
+typedef struct _SCB {
+
+ //
+ // The type and size of this record (must be NW_NTC_SCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Pointer to the non-paged part of the SCB.
+ //
+
+ struct _NONPAGED_SCB *pNpScb;
+
+ //
+ // Prefix table entry.
+ //
+
+ UNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+
+ //
+ // Server version number
+ //
+
+ UCHAR MajorVersion;
+ UCHAR MinorVersion;
+
+ //
+ // List of VCBs for this server, and a count of the VCB on the list.
+ // These fields are protected by the RCB resource.
+ //
+
+ LIST_ENTRY ScbSpecificVcbQueue;
+ ULONG VcbCount;
+
+ //
+ // A list of ICBs for the SCB.
+ //
+
+ LIST_ENTRY IcbList;
+ ULONG IcbCount;
+ ULONG OpenNdsStreams;
+
+ //
+ // User credentials that this Scb relates to.
+ //
+
+ LARGE_INTEGER UserUid;
+
+ //
+ // A count of the open files for all the VCBs for this server.
+ // Plus the number of VCB that are explicitly connected.
+ //
+
+ ULONG OpenFileCount;
+
+ //
+ // The name of the server for this SCB. Note the pNpScb->ServerName and
+ // UnicodeUid point at subparts of UidServerName->Buffer which must be
+ // non-paged pool.
+ //
+
+ UNICODE_STRING UidServerName; // L"3e7\mars312
+ UNICODE_STRING UnicodeUid; // L"3e7"
+
+ //
+ // The name of nds tree that this server belongs to, if any.
+ //
+
+ UNICODE_STRING NdsTreeName; // L"MARS"
+
+ //
+ // The username / password to use for auto-reconnect.
+ //
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+
+ //
+ // Is this the logon (preferred) server?
+ //
+
+ BOOLEAN PreferredServer;
+
+ //
+ // Is this server waiting for us to read a message?
+ //
+
+ BOOLEAN MessageWaiting;
+
+ //
+ // The number of tree connects to the root of the SCB.
+ //
+
+ ULONG AttachCount;
+
+ RTL_BITMAP DriveMapHeader;
+ ULONG DriveMap[ (MAX_DRIVES + 1) / 32 ];
+
+} SCB, *PSCB;
+
+//
+// Values for pNpScb->State
+//
+
+//
+// The SCB is on it's way up
+//
+
+#define SCB_STATE_ATTACHING (0x0001)
+
+//
+// The SCB is connected and logged in.
+//
+
+#define SCB_STATE_IN_USE (0x0003)
+
+//
+// The SCB is being disconnected or shutdown.
+//
+
+#define SCB_STATE_DISCONNECTING (0x0004)
+#define SCB_STATE_FLAG_SHUTDOWN (0x0005)
+
+//
+// The SCB is waiting to be connected.
+//
+
+#define SCB_STATE_RECONNECT_REQUIRED (0x0006)
+
+//
+// The SCB is connected but has not been logged into
+//
+
+#define SCB_STATE_LOGIN_REQUIRED (0x0007)
+
+//
+// The SCB is a fake SCB used to find a dir
+// server for a tree.
+//
+
+#define SCB_STATE_TREE_SCB (0x0008)
+
+//
+// The NONPAGED_SCB (Server control Block) contains all the data required
+// when communicating with a server when a spinlock is held or at raised
+// IRQL such as when being called at indication time by the transport.
+// This structure must be allocated from non-paged pool.
+//
+
+typedef struct _NONPAGED_SCB {
+
+ //
+ // The type and size of this record (must be NW_NTC_SCBNP
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Reference count and state information.
+ //
+
+ ULONG Reference;
+ ULONG State;
+
+ //
+ // The time this SCB was last used.
+ //
+
+ LARGE_INTEGER LastUsedTime;
+
+ //
+ // Sending is true between the IoCallDriver to send the datagram and
+ // the completion routine for the send.
+ //
+
+ BOOLEAN Sending;
+
+ //
+ // Receiving is true when the transport has indicated to the driver
+ // that there is data to receive and there is too much data to handle
+ // at indication time or we have received indicated data before
+ // the the send IRP completes.
+ //
+
+ BOOLEAN Receiving;
+
+ //
+ // Received is true when the rx data is valid. If a receive Irp is
+ // put down when Receiving is set to true then Received is set to
+ // true when the receive Irp completes.
+ //
+
+ BOOLEAN Received;
+
+ //
+ // OkToReceive is true iff pEx should be called
+ //
+
+ BOOLEAN OkToReceive;
+
+ //
+ // Older servers insist that reads and writes do not cross 4k offsets
+ // in the file.
+ //
+
+ BOOLEAN PageAlign;
+
+ //
+ // The links on the global list of SCBs.
+ //
+
+ LIST_ENTRY ScbLinks;
+
+ //
+ // Pointer to the paged component of the Scb
+ //
+
+ PSCB pScb;
+
+ //
+ // The list of request in progress for this SCB.
+ //
+
+ LIST_ENTRY Requests;
+
+ //
+ // The name of the server for this SCB.
+ //
+
+ UNICODE_STRING ServerName;
+
+ //
+ // Transport related information.
+ //
+
+ TA_IPX_ADDRESS LocalAddress;
+ TA_IPX_ADDRESS RemoteAddress;
+ TA_IPX_ADDRESS EchoAddress;
+ IPXaddress ServerAddress;
+ ULONG EchoCounter;
+
+ //
+ // Server is an autoassigned a socket in the range 0x4000 to 0x7fff.
+ // The transport assigns the socket number avoiding in-use sockets.
+ // Watchdog is socket+1 and Send is socket+2.
+ //
+
+ NW_TDI_STRUCT Server; // Used by us to contact server
+ NW_TDI_STRUCT WatchDog; // Used by the server to check on us
+ NW_TDI_STRUCT Send; // Used for send messages
+ NW_TDI_STRUCT Echo; // Used to determine max packet size
+ NW_TDI_STRUCT Burst; // Used for burst mode read and write
+
+ USHORT TickCount;
+
+ SHORT RetryCount; // Counts down to zero for current request
+ SHORT TimeOut; // ticks to retransmission of current request
+ UCHAR SequenceNo;
+ UCHAR ConnectionNo;
+ UCHAR ConnectionNoHigh;
+ UCHAR ConnectionStatus;
+ USHORT MaxTimeOut;
+ USHORT BufferSize;
+ UCHAR TaskNo;
+
+ //
+ // Burst mode parameters
+ //
+
+ ULONG SourceConnectionId; // High-low order
+ ULONG DestinationConnectionId; // High-low order
+ ULONG MaxPacketSize;
+ ULONG MaxSendSize;
+ ULONG MaxReceiveSize;
+ BOOLEAN SendBurstModeEnabled;
+ BOOLEAN ReceiveBurstModeEnabled;
+ BOOLEAN BurstRenegotiateReqd;
+ ULONG BurstSequenceNo; // Counts # of burst packets sent
+ USHORT BurstRequestNo; // Counts # of burst requests sent
+ LONG SendBurstSuccessCount; // The number of consecutive successful bursts
+ LONG ReceiveBurstSuccessCount; // The number of consecutive successful bursts
+
+ //
+ // Send delays and timeouts
+ //
+
+ SHORT SendTimeout; // Exchange timeout in ticks (1/18th sec)
+ ULONG TotalWaitTime; // Total time, in ticks, waiting for current response
+
+ LONG NwLoopTime; // Time for a small packet to reach the server and return
+ LONG NwSingleBurstPacketTime; // Time for a burst packet to go to the server
+
+ LONG NwMaxSendDelay; // Burst send delay time, in 100us units
+ LONG NwSendDelay; // Burst send delay time, in 100us units
+ LONG NwGoodSendDelay; // Burst send delay time, in 100us units
+ LONG NwBadSendDelay; // Burst send delay time, in 100us units
+ LONG BurstDataWritten; // Bytes written, used for dummy NCP in write.c
+
+ LONG NwMaxReceiveDelay; // Burst delay time, in 100us units
+ LONG NwReceiveDelay; // Burst delay time, in 100us units
+ LONG NwGoodReceiveDelay; // Burst delay time, in 100us units
+ LONG NwBadReceiveDelay; // Burst delay time, in 100us units
+
+ LONG CurrentBurstDelay; // All requests in the current burst need the same value
+
+ LARGE_INTEGER NtSendDelay; // Burst send delay time, in 100ns units
+
+ //
+ // A spin lock used to protect various fields for this SCB.
+ // NpScbInterLock is used to protect pNpScb->Reference.
+ //
+
+ KSPIN_LOCK NpScbSpinLock;
+ KSPIN_LOCK NpScbInterLock;
+
+ //
+ // This field records the last time a time-out event was written to
+ // the event log for this server.
+ //
+
+ LARGE_INTEGER NwNextEventTime;
+
+ //
+ // LIP estimation of speed in 100bps units.
+ //
+
+ ULONG LipDataSpeed;
+
+#ifdef MSWDBG
+ BOOL RequestQueued;
+ BOOL RequestDequeued;
+
+ ULONG SequenceNumber;
+#endif
+
+} NONPAGED_SCB, *PNONPAGED_SCB;
+
+//
+// Delete this VCB immediately if the reference count reaches zero.
+//
+
+#define VCB_FLAG_DELETE_IMMEDIATELY 0x00000001
+#define VCB_FLAG_EXPLICIT_CONNECTION 0x00000002
+#define VCB_FLAG_PRINT_QUEUE 0x00000004
+#define VCB_FLAG_LONG_NAME 0x00000008
+
+//
+// The VCB corresponds to a netware volume.
+//
+
+typedef struct _VCB {
+
+ //
+ // Type and size of this record (must be NW_NTC_VCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ ULONG Reference;
+ LARGE_INTEGER LastUsedTime;
+
+ //
+ // Connection the the global VCB list.
+ //
+
+ LIST_ENTRY GlobalVcbListEntry;
+ ULONG SequenceNumber;
+
+ //
+ // The requested volume name in the following form:
+ //
+ // \{Server | Tree}\{Share | Volume.Object}\Path
+ //
+
+ UNICODE_STRING Name;
+
+ //
+ // If the above name refers to an nds volume, this
+ // contains the resolved server and share name in
+ // the following form:
+ //
+ // \Server\Share\Path
+ //
+
+ UNICODE_STRING ConnectName;
+
+ //
+ // The share name in Netware compatible form.
+ //
+
+ UNICODE_STRING ShareName;
+
+ //
+ // The prefix table entry for this volume.
+ //
+
+ UNICODE_PREFIX_TABLE_ENTRY PrefixEntry; // 7 DWORDs
+
+ union {
+
+ //
+ // Disk VCB specific data.
+ //
+
+ struct {
+
+ //
+ // The volume number
+ //
+
+ CHAR VolumeNumber;
+
+ //
+ // The name space number for long name support. -1 if long name
+ // space is not supported.
+ //
+
+ CHAR LongNameSpace;
+
+ //
+ // The remote handle
+ //
+
+ CHAR Handle;
+
+ //
+ // The Drive Letter we told the server we were mapping. Portable
+ // NetWare needs this to be different for each permanent handle
+ // we create.
+ //
+
+ CHAR DriveNumber;
+
+ } Disk;
+
+ //
+ // Print VCB specific data.
+ //
+
+ struct {
+ ULONG QueueId;
+ } Print;
+
+ } Specific;
+
+ //
+ // The drive letter for this VCB. (0 if this is UNC).
+ //
+
+ WCHAR DriveLetter;
+
+ //
+ // The SCB for this volume, and a link to the VCBs for this SCB
+ //
+
+ PSCB Scb;
+ LIST_ENTRY VcbListEntry;
+
+ //
+ // List of FCBs and DCBs for this server. These fields are protected
+ // by the RCB resource.
+ //
+
+ LIST_ENTRY FcbList;
+
+ //
+ // The count of open ICBs for this VCB.
+ //
+
+ ULONG OpenFileCount;
+
+ //
+ // VCB flags
+ //
+
+ ULONG Flags;
+
+} VCB, *PVCB;
+
+//
+// Use default date / time when netware returns no info, or bogus info.
+//
+
+#define DEFAULT_DATE ( 1 + (1 << 5) + (0 << 9) ) /* Jan 1, 1980 */
+#define DEFAULT_TIME ( 0 + (0 << 5) + (0 << 11) ) /* 12:00am */
+
+//
+// The Fcb/Dcb record corresponds to every open file and directory.
+//
+// The structure is really divided into two parts. FCB can be allocated
+// from paged pool which the NONPAGED_FCB must be allocated from non-paged
+// pool.
+//
+
+typedef struct _FCB {
+
+ //
+ // Type and size of this record (must be NW_NTC_FCB or NW_NTC_DCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // The VCB for this file.
+ //
+
+ PVCB Vcb;
+
+ //
+ // The following field is the fully qualified file name for this FCB/DCB.
+ // The file name relative to the root of the volume.
+ //
+
+ UNICODE_STRING FullFileName;
+ UNICODE_STRING RelativeFileName;
+
+ //
+ // Netware file information.
+ //
+
+ USHORT LastModifiedDate;
+ USHORT LastModifiedTime;
+ USHORT CreationDate;
+ USHORT CreationTime;
+ USHORT LastAccessDate;
+
+ //
+ // The state of the FCB.
+ //
+
+ ULONG State;
+ ULONG Flags;
+
+ //
+ // A record of accesss currently granted.
+ //
+
+ SHARE_ACCESS ShareAccess;
+
+ //
+ // The prefix table entry for this file.
+ //
+
+ UNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+
+ //
+ // The SCB for this file, and a link to the FCB for this SCB
+ //
+
+ PSCB Scb;
+ LIST_ENTRY FcbListEntry;
+
+ //
+ // The list of ICB's for this FCB or DCB.
+ //
+
+ LIST_ENTRY IcbList;
+ ULONG IcbCount;
+
+ //
+ // A pointer to the specific non-paged data for the Fcb.
+ //
+
+ struct _NONPAGED_FCB *NonPagedFcb;
+
+ ULONG LastReadOffset;
+ ULONG LastReadSize;
+
+} FCB, DCB;
+typedef FCB *PFCB;
+typedef DCB *PDCB;
+
+typedef enum {
+ ReadAhead,
+ WriteBehind
+} CACHE_TYPE;
+
+typedef struct _NONPAGED_FCB {
+
+ //
+ // The following field is used for fast I/O
+ //
+ // The following comments refer to the use of the AllocationSize field
+ // of the FsRtl-defined header to the nonpaged Fcb.
+ //
+ // For a directory when we create a Dcb we will not immediately
+ // initialize the cache map, instead we will postpone it until our first
+ // call to NwReadDirectoryFile or NwPrepareWriteDirectoryFile.
+ // At that time we will search the Nw to find out the current allocation
+ // size (by calling NwLookupFileAllocationSize) and then initialize the
+ // cache map to this allocation size.
+ //
+ // For a file when we create an Fcb we will not immediately initialize
+ // the cache map, instead we will postpone it until we need it and
+ // then we determine the allocation size from either searching the
+ // fat to determine the real file allocation, or from the allocation
+ // that we've just allocated if we're creating a file.
+ //
+ // A value of -1 indicates that we do not know what the current allocation
+ // size really is, and need to examine the fat to find it. A value
+ // of than -1 is the real file/directory allocation size.
+ //
+ // Whenever we need to extend the allocation size we call
+ // NwAddFileAllocation which (if we're really extending the allocation)
+ // will modify the Nw, Rcb, and update this field. The caller
+ // of NwAddFileAllocation is then responsible for altering the Cache
+ // map size.
+ //
+
+ FSRTL_COMMON_FCB_HEADER Header;
+
+ PFCB Fcb;
+
+ //
+ // The following field contains a record of special pointers used by
+ // MM and Cache to manipluate section objects. Note that the values
+ // are set outside of the file system. However the file system on an
+ // open/create will set the file object's SectionObject field to point
+ // to this field
+ //
+
+ SECTION_OBJECT_POINTERS SegmentObject;
+
+ //
+ // The following field is used to maintain a list of locks owned for
+ // this file. It points to an ordered list of file locks.
+ //
+
+ LIST_ENTRY FileLockList;
+
+ //
+ // The following field is used to maintain a list of pending locks
+ // for this file. All locks in this list conflict with existing
+ // locks on the FileLockList.
+ //
+
+ LIST_ENTRY PendingLockList;
+
+ //
+ // A resource to synchronize access to the FCB and it's ICBs
+ //
+
+ ERESOURCE Resource;
+
+ //
+ // Netware file information.
+ //
+
+ UCHAR Attributes;
+
+ //
+ // File data cache information
+ //
+
+ UCHAR CacheType; // ReadAhead or WriteBehind
+ PUCHAR CacheBuffer; // The cache buffer
+ PMDL CacheMdl; // The full MDL for the cache buffer
+ ULONG CacheSize; // The size of the cache buffer
+ ULONG CacheFileOffset; // The file offset of this data
+ ULONG CacheDataSize; // The amount of file data in the cache
+
+} NONPAGED_FCB, NONPAGED_DCB;
+
+typedef NONPAGED_FCB *PNONPAGED_FCB;
+typedef NONPAGED_DCB *PNONPAGED_DCB;
+
+#define FCB_STATE_OPEN_PENDING 0x00000001
+#define FCB_STATE_OPENED 0x00000002
+#define FCB_STATE_CLOSE_PENDING 0x00000003
+
+#define FCB_FLAGS_DELETE_ON_CLOSE 0x00000001
+#define FCB_FLAGS_TRUNCATE_ON_CLOSE 0x00000002
+#define FCB_FLAGS_PAGING_FILE 0x00000004
+#define FCB_FLAGS_PREFIX_INSERTED 0x00000008
+#define FCB_FLAGS_FORCE_MISS_IN_PROGRESS 0x00000010
+#define FCB_FLAGS_ATTRIBUTES_ARE_VALID 0x00000020
+#define FCB_FLAGS_LONG_NAME 0x00000040
+#define FCB_FLAGS_LAZY_SET_SHAREABLE 0x00000100
+
+//
+// The Icb record is allocated for every file object
+//
+
+typedef struct _ICB {
+
+ //
+ // Type and size of this record (must be NW_NTC_ICB or NW_NTC_ICB_SCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // A link to the list of ICB's for our FCB, and our FCB.
+ //
+
+ LIST_ENTRY ListEntry;
+
+ union {
+ PFCB Fcb;
+ PSCB Scb;
+ } SuperType;
+
+ PNONPAGED_FCB NpFcb; // Valid only for node type NW_ITC_ICB
+
+ //
+ // The state of this ICB.
+ //
+
+ ULONG State;
+
+ //
+ // The remote handle;
+ //
+
+ UCHAR Handle[6]; // Keep WORD aligned.
+
+ BOOLEAN HasRemoteHandle; // TRUE if we have a remote handle for this ICB
+
+ //
+ // The file object for this ICB.
+ //
+
+ PFILE_OBJECT FileObject;
+
+ //
+ // The query template is used to filter directory query requests.
+ // It originally is set to null and on the first call the NtQueryDirectory
+ // it is set the the input filename or "*" if the name is supplied.
+ // All subsquent queries then use this template
+ //
+
+ OEM_STRING NwQueryTemplate;
+ UNICODE_STRING UQueryTemplate;
+ ULONG IndexOfLastIcbReturned;
+ UCHAR Pid;
+
+ BOOLEAN DotReturned;
+ BOOLEAN DotDotReturned;
+ BOOLEAN ReturnedSomething;
+ BOOLEAN ShortNameSearch;
+
+ //
+ // More search parameters.
+ //
+
+ USHORT SearchHandle;
+ UCHAR SearchVolume;
+ UCHAR SearchAttributes;
+
+ //
+ // Extra search parameters for long name support
+ //
+
+ ULONG SearchIndexLow;
+ ULONG SearchIndexHigh;
+
+ //
+ // SVR to avoid rescanning from end of dir all
+ // the way through the directory again.
+ //
+
+ ULONG LastSearchIndexLow;
+
+ // SVR end
+
+ //
+ // Print parametres;
+ //
+
+ BOOLEAN IsPrintJob;
+ USHORT JobId;
+ BOOLEAN ActuallyPrinted;
+
+ //
+ // This flag prevents cleanup from updating the access time.
+ //
+
+ BOOLEAN UserSetLastAccessTime;
+
+ //
+ // The current file position.
+ //
+
+ ULONG FilePosition;
+
+ //
+ // The size of the file if its ICB_SCB
+ //
+
+ ULONG FileSize;
+
+ //
+ // The Next dirent offset is used by directory enumeration. It is
+ // the offset (within the directory file) of the next dirent to examine.
+ //
+
+ //VBO OffsetToStartSearchFrom;
+
+ //
+ // If this ICB was created with OPEN_RENAME_TARGET then the following
+ // parameters are used
+ //
+
+ BOOLEAN IsAFile;
+ BOOLEAN Exists;
+ BOOLEAN FailedFindNotify;
+
+ //
+ // Is this a tree handle? We need to know for delete.
+ //
+ BOOLEAN IsTreeHandle;
+
+} ICB, *PICB;
+
+#define ICB_STATE_OPEN_PENDING 0x00000001
+#define ICB_STATE_OPENED 0x00000002
+#define ICB_STATE_CLEANED_UP 0x00000003
+#define ICB_STATE_CLOSE_PENDING 0x00000004
+
+#define INVALID_PID 0
+
+//
+// A structure used to maintain a list of file locks.
+//
+
+typedef struct _NW_FILE_LOCK {
+
+ //
+ // Type and size of this record (must be NW_NTC_FILE_LOCK )
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // A link to the list of locks for this FCB.
+ //
+
+ LIST_ENTRY ListEntry;
+
+ //
+ // The ICB this lock belongs to.
+ //
+
+ PICB Icb;
+
+ //
+ // The IRP Context for this lock request.
+ //
+
+ struct _IRP_CONTEXT *IrpContext;
+
+ //
+ // The lock offset, length, and key.
+ //
+
+ LONG StartFileOffset;
+ ULONG Length;
+ LONG EndFileOffset;
+ ULONG Key;
+ USHORT Flags;
+
+} NW_FILE_LOCK, *PNW_FILE_LOCK;
+
+//
+// The Rcb record controls access to the redirector device
+//
+
+typedef struct _RCB {
+
+ //
+ // Type and size of this record (must be NW_NTC_RCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // The run state of the redirector
+ //
+
+ ULONG State;
+
+ //
+ // The count of open handles to the RCB.
+ // Access is protected by the RCB Resource.
+ //
+
+ ULONG OpenCount;
+
+ //
+ // A resource to synchronize access to the RCB.
+ //
+
+ ERESOURCE Resource;
+
+ //
+ // A record of accesss currently granted to the RCB.
+ //
+
+ SHARE_ACCESS ShareAccess;
+
+ //
+ // A prefix table of all connected servers.
+ //
+
+ UNICODE_PREFIX_TABLE ServerNameTable;
+
+ //
+ // A prefix table of all open volumes.
+ //
+
+ UNICODE_PREFIX_TABLE VolumeNameTable;
+
+ //
+ // A prefix table of all open files
+ //
+
+ UNICODE_PREFIX_TABLE FileNameTable;
+
+} RCB, *PRCB;
+
+
+#define RCB_STATE_STOPPED 0x00000001
+#define RCB_STATE_STARTING 0x00000002
+#define RCB_STATE_NEED_BIND 0x00000003
+#define RCB_STATE_RUNNING 0x00000004
+#define RCB_STATE_SHUTDOWN 0x00000005
+
+//
+// IRP_CONTEXT Flags bits.
+//
+
+#define IRP_FLAG_IN_FSD 0x00000001 // This IRP is being process in the FSD
+#define IRP_FLAG_ON_SCB_QUEUE 0x00000002 // This IRP is queued to an SCB
+#define IRP_FLAG_SEQUENCE_NO_REQUIRED 0x00000004 // This packet requires a sequence #
+#define IRP_FLAG_SIGNAL_EVENT 0x00000010
+#define IRP_FLAG_RETRY_SEND 0x00000020 // We are resending a timed out request
+#define IRP_FLAG_RECONNECTABLE 0x00000040 // We are allowed to try a reconnect if this request fails due to a bad connection
+#define IRP_FLAG_RECONNECT_ATTEMPT 0x00000080 // This IRP is being used to attempt a reconnect
+#define IRP_FLAG_BURST_REQUEST 0x00000100 // This is a burst request packet
+#define IRP_FLAG_BURST_PACKET 0x00000200 // This is any burst packet
+#define IRP_FLAG_NOT_OK_TO_RECEIVE 0x00000400 // Don't set ok to receive when sending this packet
+#define IRP_FLAG_REROUTE_ATTEMPTED 0x00000800 // A re-route has been attempted for this packet
+#define IRP_FLAG_BURST_WRITE 0x00001000 // We are processsing a burst write request
+#define IRP_FLAG_SEND_ALWAYS 0x00002000 // Okay to send this packet, even if RCB State is shutdown
+#define IRP_FLAG_FREE_RECEIVE_MDL 0x00004000 // Free the receive irp's MDL when the irp completes
+#define IRP_FLAG_NOT_SYSTEM_PACKET 0x00008000 // Used in burst writes to alternate system packet and normal
+#define IRP_FLAG_NOCONNECT 0x00010000 // Used to inspect server list
+
+typedef struct _IRP_CONTEXT {
+
+ //
+ // Type and size of this record (must be NW_NTC_IRP_CONTEXT).
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Information about this IRP
+ //
+
+ ULONG Flags;
+
+ //
+ // This structure is used for posting to the Ex worker threads.
+ //
+
+ WORK_QUEUE_ITEM WorkQueueItem; // 4*sizeof(ULONG)
+
+ // Workspace for exchange()
+ PACKET_TYPE PacketType;
+
+ //
+ // Server Control Block to which this request applies.
+ //
+
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+
+ //
+ // The socket structure to use for this request. If NULL, use
+ // pNpScb->Server socket.
+ //
+
+ PNW_TDI_STRUCT pTdiStruct;
+
+ //
+ // List of requests to a particular server. Listed on Scb->Requests.
+ //
+
+ LIST_ENTRY NextRequest;
+
+ //
+ // Used for processing synchronous IRPs.
+ //
+
+ KEVENT Event; // 4 words
+
+ //
+ // A pointer to the originating Irp and its original contents when
+ // the I/O system submitted it to the rdr.
+ //
+
+ PIRP pOriginalIrp;
+ PVOID pOriginalSystemBuffer;
+ PVOID pOriginalUserBuffer;
+ PMDL pOriginalMdlAddress;
+
+ //
+ // Information used if we need to post an IRP to process the receive
+ //
+
+ PIRP ReceiveIrp;
+
+ //
+ // Pointer to the Mdl used to transmit/receive the Ncp header.
+ //
+
+ PMDL TxMdl;
+ PMDL RxMdl;
+
+ //
+ // Routine to run when this IRP context reaches the front of the
+ // SCB queue.
+ //
+
+ PRUN_ROUTINE RunRoutine;
+
+ //
+ // Routine to handle the response Ncp
+ //
+
+ PEX pEx;
+
+ //
+ // Routine to handle packet receipt
+ //
+
+ PRECEIVE_ROUTINE ReceiveDataRoutine;
+
+ //
+ // Routine to handle FSP post processing.
+ //
+
+ PPOST_PROCESSOR PostProcessRoutine;
+
+ //
+ // Routine to run when this IRP context times out while on the SCB
+ // queue.
+ //
+
+ PRUN_ROUTINE TimeoutRoutine;
+
+ //
+ // Routine to run when this IRP has completed a send.
+ //
+
+ PIO_COMPLETION_ROUTINE CompletionSendRoutine;
+
+ //
+ // Work Item used for scheduling reconnect.
+ //
+
+ PWORK_QUEUE_ITEM pWorkItem;
+
+ //
+ // Buffer used to hold the Ncb to be transmitted/received.
+ //
+
+ ULONG Signature1;
+
+ UCHAR req[MAX_SEND_DATA];
+ ULONG Signature2;
+
+ ULONG ResponseLength;
+ UCHAR rsp[MAX_RECV_DATA];
+ ULONG Signature3;
+
+ //
+ // Address to be used in the Send Datagram.
+ //
+
+ TA_IPX_ADDRESS Destination;
+ TDI_CONNECTION_INFORMATION ConnectionInformation; // Remote server
+
+ //
+ // The ICB being processed.
+ //
+
+ PICB Icb;
+
+ //
+ // Per IRP processor information. A handy place to store information
+ // for the IRP in progress.
+ //
+
+ union {
+ struct {
+ UNICODE_STRING FullPathName;
+ UNICODE_STRING VolumeName;
+ UNICODE_STRING PathName;
+ UNICODE_STRING FileName;
+ BOOLEAN NdsCreate;
+ BOOLEAN NeedNdsData;
+ DWORD dwNdsOid;
+ DWORD dwNdsObjectType;
+ DWORD dwNdsShareLength;
+ UNICODE_STRING UidConnectName;
+ WCHAR DriveLetter;
+ ULONG ShareType;
+ BOOLEAN fExCredentialCreate;
+ PUNICODE_STRING puCredentialName;
+ PCHAR FindNearestResponse[4];
+ ULONG FindNearestResponseCount;
+ LARGE_INTEGER UserUid;
+ } Create;
+
+ struct {
+ PVOID Buffer;
+ ULONG Length;
+ PVCB Vcb;
+ CHAR VolumeNumber;
+ } QueryVolumeInformation;
+
+ struct {
+ PVOID Buffer;
+ ULONG Length;
+ PMDL InputMdl;
+ UCHAR Function; // Used for special case post-processing
+ UCHAR Subfunction; // during UserNcpCallback
+
+ } FileSystemControl;
+
+ struct {
+ PVOID Buffer;
+ ULONG WriteOffset;
+ ULONG RemainingLength;
+ PMDL PartialMdl;
+ PMDL FullMdl;
+ ULONG FileOffset;
+ ULONG LastWriteLength;
+
+ ULONG BurstOffset;
+ ULONG BurstLength;
+ NTSTATUS Status;
+
+ ULONG TotalWriteLength;
+ ULONG TotalWriteOffset;
+
+ ULONG PacketCount;
+ } Write;
+
+ struct {
+ ULONG CacheReadSize; // Amount of data read from the cache
+ ULONG ReadAheadSize; // Extra data to read
+
+ PVOID Buffer; // Buffer for the current read
+ PMDL FullMdl;
+ PMDL PartialMdl;
+ ULONG ReadOffset;
+ ULONG RemainingLength;
+ ULONG FileOffset;
+ ULONG LastReadLength;
+
+ LIST_ENTRY PacketList; // List of packets received
+ ULONG BurstRequestOffset; // Offset in burst buffer for last request
+ ULONG BurstSize; // Number of bytes in current burst
+ PVOID BurstBuffer; // Buffer for the current burst
+ BOOLEAN DataReceived;
+ NTSTATUS Status;
+ UCHAR Flags;
+
+ ULONG TotalReadLength;
+ ULONG TotalReadOffset;
+ } Read;
+
+ struct {
+ PNW_FILE_LOCK FileLock;
+ ULONG Key;
+ BOOLEAN Wait;
+ BOOLEAN ByKey;
+ PLIST_ENTRY LastLock;
+ } Lock;
+
+ } Specific;
+
+ struct {
+ UCHAR Error;
+ } ResponseParameters;
+
+#ifdef NWDBG
+ ULONG DebugValue;
+ ULONG SequenceNumber;
+#endif
+} IRP_CONTEXT, *PIRP_CONTEXT;
+
+typedef struct _BURST_READ_ENTRY {
+ LIST_ENTRY ListEntry;
+ ULONG DataOffset;
+ USHORT ByteCount;
+} BURST_READ_ENTRY, *PBURST_READ_ENTRY;
+
+typedef struct _LOGON {
+
+ //
+ // The type and size of this record.
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // List of Login records.
+ //
+
+ LIST_ENTRY Next;
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING PassWord;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER UserUid;
+
+ //
+ // The NDS credential list, default tree,
+ // and default context for this user.
+ //
+
+ ERESOURCE CredentialListResource;
+ LIST_ENTRY NdsCredentialList;
+
+} LOGON, *PLOGON;
+
+typedef struct _MINI_IRP_CONTEXT {
+
+ //
+ // Header information
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // A link to queue IRP contexts
+ //
+
+ LIST_ENTRY Next;
+
+ PIRP_CONTEXT IrpContext;
+ PIRP Irp;
+
+ PVOID Buffer; // The buffer for this request.
+ PMDL Mdl1; // The MDL for the buffer
+ PMDL Mdl2; // The MDL for the data
+} MINI_IRP_CONTEXT, *PMINI_IRP_CONTEXT;
+
+//
+// Definitions for unlockable code sections.
+//
+
+typedef struct _SECTION_DESCRIPTOR {
+ PVOID Base;
+ PVOID Handle;
+ ULONG ReferenceCount;
+} SECTION_DESCRIPTOR, *PSECTION_DESCRIPTOR;
+
+#endif // _NWSTRUC_
+
diff --git a/private/nw/rdr/synch.txt b/private/nw/rdr/synch.txt
new file mode 100644
index 000000000..82d68ee1a
--- /dev/null
+++ b/private/nw/rdr/synch.txt
@@ -0,0 +1,74 @@
+This file describes use of resources and spin locks with the netware
+redirector. There are 2 major sections - global locks, and per structure
+locks. Each subsection names a lock or resources and list all of the
+global variable, and structure fields it protects.
+
+
+1. Global Locks
+
+1.1 ScbSpinLock
+ - ScbQueue
+ - NpScb fields -> ScbLinks
+ - Nearest server table.
+
+1.2 Rcb->Resource
+ - ServerNameTable
+ - FileNameTable, Fcb->PrefixEntry
+ - Rcb fields -> OpenCount
+ - Synchronize access to newly created FCBs
+ - DefaultUserName, DefaultPassword, DefaultServerName.
+ - Fcb->IcbList, Icb->ListEntry
+ - DriveMapTable
+ - Vcb->ReferenceCount, Vcb->FcbList, Vcb->FcbCount, GlobalVcbListEntry
+ - Scb->ScbSpecificVcbQueue, Scb->VcbCount, OpenFileCount, AttachCount
+ - GlobalVcbList, CurrentVcbEntry
+
+1.3 NwScavengerSpinLock
+ - NwScavengerTickCount
+
+1.4 Front of the SCB queue
+ - Icb->State, HasRemoteHandle
+ - NpScb->State
+ - pScb->UserName, pScb->Password
+ - Fcb->FileSize (except during create)
+
+1.5 NwMessageSpinLock
+ - NwGetMessageList
+
+1.6 NwPendingLockSpinLock
+ - NwPendingLockList
+
+1.7 NwFcbTableResource
+ - FCB / ICB creation.
+
+2. Per structure locks
+
+2.1 SCB->NpScbSpinLock
+ - NpScb fields -> Sending, Receiving, OkToReceive, TimeOut,
+ MaxTimeOut, Requests, RetryCount, Reference
+
+2.2 FCB->Resource
+ - Fcb-> Attributes, LastModifiedDate, LastModifiedTime
+ State, FileLockList, PendingLockList,
+
+2.3 Front of the SCB queue
+ - Icb->FileLockList
+
+2.4 LOGON->CredListResource
+ Protects the credentials on the cred list
+ and the list pointers. Acquire shared for
+ read access to the list. Acquire exclusive
+ for write access to the list.
+
+In order to eliminate dead locks, locks should be acquired in the following
+order (if multiple locks are needed).
+
+RCB->Resource
+FCB->Resource
+NwScavengerSpinLock
+LOGON->CredListResource
+ScbSpinLock
+SCB->NpScbSpinLock
+
+A thread cannot wait for the SCB queue while holding any other lock.
+
diff --git a/private/nw/rdr/timer.c b/private/nw/rdr/timer.c
new file mode 100644
index 000000000..e5ca1204c
--- /dev/null
+++ b/private/nw/rdr/timer.c
@@ -0,0 +1,537 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ This module contains code which implements the receive and send timeouts
+ for each connection.
+
+Author:
+
+ Colin Watson (ColinW) 21-Feb-1993
+
+Environment:
+
+ Kernel mode
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_TIMER)
+
+LARGE_INTEGER DueTime;
+KDPC NwDpc; // DPC object for timeouts.
+KTIMER Timer; // kernel timer for this request.
+ULONG ScavengerTickCount;
+
+BOOLEAN WorkerRunning = FALSE;
+WORK_QUEUE_ITEM WorkItem;
+
+#ifdef NWDBG
+BOOLEAN DisableTimer = FALSE;
+#endif
+
+//
+// When we want to stop the timer, set TimerStop to TRUE. When the timer
+// is stopped TimerStopped will be set to the signalled state.
+//
+
+BOOLEAN TimerStop;
+KEVENT TimerStopped;
+
+VOID
+TimerDPC(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, StartTimer )
+#pragma alloc_text( PAGE, StopTimer )
+
+#endif
+
+
+VOID
+StartTimer(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine starts the timer ticking.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // We need 18.21 ticks per second
+ //
+
+ DueTime.QuadPart = (( 100000 * MILLISECONDS ) / 1821) * -1;
+
+ //
+ // This is the first connection with timeouts specified.
+ // Set up the timer so that every 500 milliseconds we scan all the
+ // connections for timed out receive and sends.
+ //
+
+ TimerStop = FALSE;
+
+ KeInitializeEvent( &TimerStopped, SynchronizationEvent, FALSE );
+ KeInitializeDpc( &NwDpc, TimerDPC, NULL );
+ KeInitializeTimer( &Timer );
+
+ (VOID)KeSetTimer(&Timer, DueTime, &NwDpc);
+
+ DebugTrace(+0, Dbg, "StartTimer\n", 0);
+}
+
+
+VOID
+StopTimer(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine stops the timer. It blocks until the timer has stopped.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (TimerStop == FALSE) {
+ TimerStop = TRUE;
+
+ DebugTrace(+0, Dbg, "StopTimer\n", 0);
+ KeWaitForSingleObject (&TimerStopped, Executive, KernelMode, FALSE, NULL);
+ }
+}
+
+
+VOID
+TimerDPC(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to search for timed out send and receive
+ requests. This routine is called at DPC level.
+
+Arguments:
+
+ Dpc - Unused.
+ Context - Unused.
+ SystemArgument1 - Unused.
+ SystemArgument2 - Unused.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY NextScbQueueEntry;
+ PLIST_ENTRY IrpContextEntry;
+ PLIST_ENTRY NextIrpContextEntry;
+ SHORT RetryCount;
+ PIRP_CONTEXT pIrpContext;
+ LARGE_INTEGER CurrentTime = {0, 0};
+ WCHAR AnonymousName[] = L"UNKNOWN";
+ PWCHAR ServerLogName;
+
+ if ( TimerStop ) {
+ KeSetEvent( &TimerStopped, 0, FALSE );
+ return;
+ }
+
+ //
+ // For each Server see if there is a timeout to process.
+ //
+
+#ifdef NWDBG
+ if ( DisableTimer ) {
+ //
+ // Reset the timer to run for another tick.
+ //
+
+ (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
+
+ return;
+ }
+#endif
+
+ //DebugTrace(+1, Dbg, "TimerDpc....\n", 0);
+
+ //
+ // Scan through the Scb's looking timed out requests.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ if (ScbQueueEntry != &ScbQueue) {
+ PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+ NwQuietReferenceScb( pNpScb );
+ }
+
+ for (;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+
+ // Obtain a pointer to the next SCB in the SCB list before
+ // dereferencing the current one.
+ //
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if (NextScbQueueEntry != &ScbQueue) {
+ PNONPAGED_SCB pNextNpScb = CONTAINING_RECORD(NextScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+ //
+ // Reference the next entry in the list to ensure the scavenger
+ // doesn't put it on another list or destroy it.
+ //
+
+ NwQuietReferenceScb( pNextNpScb );
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
+
+ //
+ // Acquire the Scb specific spin lock to protect access
+ // the the Scb fields.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &pNpScb->NpScbSpinLock );
+
+ //
+ // Look at the first request on the queue only (since it is
+ // the only active request).
+ //
+
+ if ( ( !IsListEmpty( &pNpScb->Requests )) &&
+ ( !pNpScb->Sending ) &&
+ ( pNpScb->OkToReceive ) &&
+ ( --pNpScb->TimeOut <= 0 ) ) {
+
+ PIRP_CONTEXT pIrpContext;
+
+ //
+ // This request has timed out. Try to retransmit the request.
+ //
+
+ pIrpContext = CONTAINING_RECORD(
+ pNpScb->Requests.Flink,
+ IRP_CONTEXT,
+ NextRequest);
+
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+
+ //
+ // Check the retry count while we own the spin lock.
+ //
+
+ RetryCount = --pNpScb->RetryCount;
+ NwQuietDereferenceScb( pNpScb );
+
+ //
+ // Set OkToReceive to FALSE, so that if we receive a response
+ // right now, our receive handler won't handle the response
+ // and cause IRP context to be freed.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+ KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
+
+ if ( pIrpContext->pOriginalIrp->Cancel ) {
+
+ //
+ // This IRP has been cancelled. Call the callback routine.
+ //
+ // BUGBUG - This will cause a timeout error.
+ //
+
+ DebugTrace(+0, Dbg, "Timer cancel IRP %X\n", pIrpContext->pOriginalIrp );
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ } else if ( RetryCount >= 0) {
+
+ //
+ // We're not out of retries. Resend the request packet.
+ //
+ // First adjust the send timeout up. Adjust the timeout
+ // more slowly on a close by server.
+ //
+
+ if ( pNpScb->SendTimeout < pNpScb->MaxTimeOut ) {
+ if ( pNpScb->TickCount <= 4 ) {
+ pNpScb->SendTimeout++;
+ } else {
+ pNpScb->SendTimeout = pNpScb->SendTimeout * 3 / 2;
+ if ( pNpScb->SendTimeout > pNpScb->MaxTimeOut ) {
+ pNpScb->SendTimeout = pNpScb->MaxTimeOut;
+ }
+ }
+ }
+
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+ DebugTrace(+0, Dbg, "Adjusting send timeout: %x\n", pIrpContext );
+ DebugTrace(+0, Dbg, "Adjusting send timeout to: %d\n", pNpScb->TimeOut );
+
+ if ( pIrpContext->TimeoutRoutine != NULL ) {
+
+ DebugTrace(+0, Dbg, "Timeout Routine, retry %x\n", RetryCount+1);
+ DebugTrace(+0, Dbg, "Calling TimeoutRoutine, %x\n", pIrpContext->TimeoutRoutine);
+ pIrpContext->TimeoutRoutine( pIrpContext );
+
+ } else {
+
+ DebugTrace(+0, Dbg, "Resending Packet, retry %x\n", RetryCount+1);
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+ SendNow( pIrpContext );
+ }
+
+ Stats.FailedSessions++;
+
+ } else {
+
+ ASSERT( pIrpContext->pEx != NULL );
+
+ //
+ // We are out of retries.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED ) ||
+ ( NwAbsoluteTotalWaitTime != 0 &&
+ pNpScb->TotalWaitTime >= NwAbsoluteTotalWaitTime ) ) {
+
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // He have already attempted to reroute the request.
+ // Give up.
+ //
+
+ DebugTrace(+0, Dbg, "Abandon Exchange\n", 0 );
+
+ if ( pIrpContext->pNpScb != &NwPermanentNpScb ) {
+
+ //
+ // Reset to the attaching state. If the server
+ // is dead, the next attempt to open a handle will
+ // fail with a better error than unexpected network
+ // error.
+ //
+
+ pIrpContext->pNpScb->State = SCB_STATE_ATTACHING;
+
+ //
+ // Determine the CurrentTime. We need to know if
+ // TimeOutEventInterval minutes have passed before
+ // we log the next time-out event.
+ //
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+
+ if ( pNpScb->ServerName.Buffer != NULL ) {
+ ServerLogName = pNpScb->ServerName.Buffer;
+ } else {
+ ServerLogName = &AnonymousName[0];
+ }
+
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ ServerLogName );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+ }
+
+ }
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ } else {
+
+ //
+ // Attempt to reroute the request.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( BooleanFlagOn(
+ pIrpContext->Flags,
+ IRP_FLAG_BURST_PACKET ) ) {
+ pIrpContext->PostProcessRoutine = NewRouteBurstRetry;
+ } else {
+ pIrpContext->PostProcessRoutine = NewRouteRetry;
+ }
+
+ NwPostToFsp( pIrpContext, FALSE );
+ }
+ }
+
+ } else {
+
+ if ( ( !IsListEmpty( &pNpScb->Requests )) &&
+ ( !pNpScb->Sending ) &&
+ ( pNpScb->OkToReceive ) ) {
+
+ DebugTrace( 0, Dbg, "TimeOut %d\n", pNpScb->TimeOut );
+ }
+
+ //
+ // Nothing to do for this SCB. Dereference this SCB and
+ // release the spin lock.
+ //
+
+ KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
+ NwQuietDereferenceScb( pNpScb );
+ }
+
+ KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
+
+ //
+ // Now see if the scavenger routine needs to be run.
+ // Only ever queue one workitem.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ NwScavengerTickCount++;
+ if (( !WorkerRunning ) &&
+ ( NwScavengerTickCount > NwScavengerTickRunCount )) {
+
+ ExInitializeWorkItem( &WorkItem, NwScavengerRoutine, &WorkItem );
+ ExQueueWorkItem( &WorkItem, DelayedWorkQueue );
+ NwScavengerTickCount = 0;
+ WorkerRunning = TRUE;
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ //
+ // Scan the list of pending locks, looking for locks to retry.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwPendingLockSpinLock );
+
+ for (IrpContextEntry = NwPendingLockList.Flink ;
+ IrpContextEntry != &NwPendingLockList ;
+ IrpContextEntry = NextIrpContextEntry ) {
+
+ NextIrpContextEntry = IrpContextEntry->Flink;
+ pIrpContext = CONTAINING_RECORD( IrpContextEntry, IRP_CONTEXT, NextRequest );
+
+ //
+ // BUGBUG surely we can't use the key like this to control the number
+ // of retries.
+ //
+
+ if ( --pIrpContext->Specific.Lock.Key <= 0 ) {
+
+ //
+ // Remove the IRP Context from the queue and reattempt the lock.
+ // Set the SEQUENCE_NO_REQUIRED flag so that the packet gets
+ // renumbered.
+ //
+
+ RemoveEntryList( &pIrpContext->NextRequest );
+ SetFlag( pIrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ PrepareAndSendPacket( pIrpContext );
+ }
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &NwPendingLockSpinLock );
+
+ //
+ // Reset the timer to run for another tick.
+ //
+
+ (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
+
+ //DebugTrace(-1, Dbg, "TimerDpc\n", 0);
+ return;
+
+ UNREFERENCED_PARAMETER (Dpc);
+ UNREFERENCED_PARAMETER (Context);
+ UNREFERENCED_PARAMETER (SystemArgument1);
+ UNREFERENCED_PARAMETER (SystemArgument2);
+
+}
+
+
diff --git a/private/nw/rdr/util.c b/private/nw/rdr/util.c
new file mode 100644
index 000000000..54da848dc
--- /dev/null
+++ b/private/nw/rdr/util.c
@@ -0,0 +1,385 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Util.c
+
+Abstract:
+
+ This module contains utilities function for the netware redirector.
+
+Author:
+
+ Manny Weiser [MannyW] 07-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CONVERT)
+
+#ifdef ALLOC_PRAGMA
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, CopyBufferToMdl )
+#endif
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+VOID
+CopyBufferToMdl(
+ PMDL DestinationMdl,
+ ULONG DataOffset,
+ PUCHAR SourceData,
+ ULONG SourceByteCount
+ )
+/*++
+
+Routine Description:
+
+ This routine copies data from a buffer described by a pointer to a
+ given offset in a buffer described by an MDL.
+
+Arguments:
+
+ DestinationMdl - The MDL for the destination buffer.
+
+ DataOffset - The offset into the destination buffer to copy the data.
+
+ SourceData - A pointer to the source data buffer.
+
+ SourceByteCount - The number of bytes to copy.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG BufferOffset;
+ ULONG PreviousBufferOffset;
+ PMDL Mdl;
+ ULONG BytesToCopy;
+ ULONG MdlByteCount;
+ PVOID pSystemVa;
+
+ DebugTrace( +1, Dbg, "MdlMoveMemory...\n", 0 );
+ DebugTrace( 0, Dbg, "Desitination MDL = %X\n", DestinationMdl );
+ DebugTrace( 0, Dbg, "DataOffset = %d\n", DataOffset );
+ DebugTrace( 0, Dbg, "SourceData = %X\n", SourceData );
+ DebugTrace( 0, Dbg, "SourceByteCount = %d\n", SourceByteCount );
+
+ BufferOffset = 0;
+
+ Mdl = DestinationMdl;
+
+ //
+ // Truncate the response if it is too big.
+ //
+
+ MdlByteCount = MdlLength( Mdl );
+ if ( SourceByteCount + DataOffset > MdlByteCount ) {
+ SourceByteCount = MdlByteCount - DataOffset;
+ }
+
+ while ( Mdl != NULL && SourceByteCount != 0 ) {
+
+ PreviousBufferOffset = BufferOffset;
+ BufferOffset += MmGetMdlByteCount( Mdl );
+
+ if ( DataOffset < BufferOffset ) {
+
+ //
+ // Copy the data to this buffer
+ //
+
+ while ( SourceByteCount > 0 ) {
+
+ BytesToCopy = MIN( SourceByteCount,
+ BufferOffset - DataOffset );
+
+ pSystemVa = MmGetSystemAddressForMdl( Mdl );
+
+ DebugTrace( 0, Dbg, "Copy to %X\n", (PUCHAR) pSystemVa +
+ DataOffset -
+ PreviousBufferOffset );
+ DebugTrace( 0, Dbg, "Copy from %X\n", SourceData );
+ DebugTrace( 0, Dbg, "Copy bytes %d\n", BytesToCopy );
+
+ TdiCopyLookaheadData(
+ (PUCHAR)pSystemVa + DataOffset - PreviousBufferOffset,
+ SourceData,
+ BytesToCopy,
+ 0 );
+
+ SourceData += BytesToCopy;
+ DataOffset += BytesToCopy;
+ SourceByteCount -= BytesToCopy;
+
+ Mdl = Mdl->Next;
+ if ( Mdl != NULL ) {
+ PreviousBufferOffset = BufferOffset;
+ BufferOffset += MmGetMdlByteCount( Mdl );
+ } else {
+ ASSERT( SourceByteCount == 0 );
+ }
+ }
+
+ } else {
+
+ Mdl = Mdl->Next;
+
+ }
+ }
+
+ DebugTrace( -1, Dbg, "MdlMoveMemory -> VOID\n", 0 );
+}
+
+//
+// These parsing routines are used to do multiple credential
+// connects to a single server.
+//
+
+NTSTATUS
+GetCredentialFromServerName(
+ IN PUNICODE_STRING puServerName,
+ OUT PUNICODE_STRING puCredentialName
+)
+/*+++
+
+ Description: Given a munged server(credential) name,
+ this routine returns the credential.
+---*/
+{
+
+ DWORD NameLength = 0;
+ BOOLEAN FoundFirstParen = FALSE;
+ BOOLEAN FoundLastParen = FALSE;
+
+ DebugTrace( 0, Dbg, "GetCredentialFromServerName: %wZ\n", puServerName );
+
+ puCredentialName->Length = puServerName->Length;
+ puCredentialName->Buffer = puServerName->Buffer;
+
+ //
+ // Find the first paren.
+ //
+
+ while ( ( puCredentialName->Length ) && !FoundFirstParen ) {
+
+ if ( puCredentialName->Buffer[0] == L'(' ) {
+ FoundFirstParen = TRUE;
+ }
+
+ puCredentialName->Buffer++;
+ puCredentialName->Length -= sizeof( WCHAR );
+ }
+
+ if ( !FoundFirstParen ) {
+ DebugTrace( 0, Dbg, "No opening paren for server(credential) name.\n", 0 );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Figure out the name length.
+ //
+
+ while ( ( puCredentialName->Length ) && !FoundLastParen ) {
+
+ if ( puCredentialName->Buffer[NameLength] == L')' ) {
+ FoundLastParen = TRUE;
+ }
+
+ NameLength++;
+ puCredentialName->Length -= sizeof( WCHAR );
+ }
+
+ if ( !FoundLastParen ) {
+ DebugTrace( 0, Dbg, "No closing paren for server(credential) name.\n", 0 );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Format the name and return. Don't count the closing paren.
+ //
+
+ NameLength--;
+
+ if ( !NameLength ) {
+ DebugTrace( 0, Dbg, "Null credential name.\n", 0 );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ puCredentialName->Length = (USHORT) (NameLength * sizeof( WCHAR ));
+ puCredentialName->MaximumLength = puCredentialName->Length;
+
+ DebugTrace( 0, Dbg, "GetCredentialFromServerName --> %wZ\n", puCredentialName );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+BuildExCredentialServerName(
+ IN PUNICODE_STRING puServerName,
+ IN PUNICODE_STRING puUserName,
+ OUT PUNICODE_STRING puExCredServerName
+)
+/*+++
+
+Description:
+
+ Takes a server name and a user name and makes an
+ ExCredServerName, which is simply: server(user)
+
+ This routine allocates memory for the credential
+ server name and the caller is responsible for
+ freeing the memory when it is no longer needed.
+
+---*/
+{
+
+ NTSTATUS Status;
+ PBYTE pbCredNameBuffer;
+
+ DebugTrace( 0, Dbg, "BuildExCredentialServerName\n", 0 );
+
+ if ( ( !puExCredServerName ) ||
+ ( !puServerName ) ||
+ ( !puUserName ) ) {
+
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "BuildExCredentialServerName -> STATUS_INVALID_PARAMETER\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ puExCredServerName->MaximumLength = puServerName->Length +
+ puUserName->Length +
+ ( 2 * sizeof( WCHAR ) );
+
+ pbCredNameBuffer = ALLOCATE_POOL( PagedPool,
+ puExCredServerName->MaximumLength );
+
+ if ( pbCredNameBuffer == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ puExCredServerName->Buffer = (PWCHAR) pbCredNameBuffer;
+ puExCredServerName->Length = puExCredServerName->MaximumLength;
+
+ //
+ // Copy over the server name.
+ //
+
+ RtlCopyMemory( pbCredNameBuffer,
+ puServerName->Buffer,
+ puServerName->Length );
+
+ pbCredNameBuffer += puServerName->Length;
+
+ //
+ // Add the credential name in parenthesis.
+ //
+
+ *( (PWCHAR) pbCredNameBuffer ) = L'(';
+
+ pbCredNameBuffer += sizeof( WCHAR );
+
+ RtlCopyMemory( pbCredNameBuffer,
+ puUserName->Buffer,
+ puUserName->Length );
+
+ pbCredNameBuffer += puUserName->Length;
+
+ *( (PWCHAR) pbCredNameBuffer ) = L')';
+
+ DebugTrace( 0, Dbg, "BuildExCredentialServerName: %wZ\n", puExCredServerName );
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+UnmungeCredentialName(
+ IN PUNICODE_STRING puCredName,
+ OUT PUNICODE_STRING puServerName
+)
+/*+++
+
+Description:
+
+ Given server(username), return the server
+ name portion.
+
+---*/
+{
+
+ USHORT Length = 0;
+
+ DebugTrace( 0, Dbg, "UnmungeCredentialName: %wZ\n", puCredName );
+
+ puServerName->Buffer = puCredName->Buffer;
+ puServerName->MaximumLength = puCredName->MaximumLength;
+
+ while ( Length < ( puCredName->Length / sizeof( WCHAR ) ) ) {
+
+ //
+ // Look for the opening paren.
+ //
+
+ if ( puCredName->Buffer[Length] == L'(' ) {
+ break;
+ }
+
+ Length++;
+ }
+
+ puServerName->Length = Length * sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, " -> %wZ\n", puServerName );
+ return STATUS_SUCCESS;
+
+}
+
+BOOLEAN
+IsCredentialName(
+ IN PUNICODE_STRING puObjectName
+)
+/*+++
+
+Description: This returns TRUE if the object is an extended
+ credential munged name.
+
+---*/
+{
+
+ DWORD dwCurrent = 0;
+
+ if ( !puObjectName ) {
+ return FALSE;
+ }
+
+ while ( dwCurrent < ( puObjectName->Length ) / sizeof( WCHAR ) ) {
+
+ if ( puObjectName->Buffer[dwCurrent] == L'(' ) {
+ return TRUE;
+ }
+
+ dwCurrent++;
+ }
+
+ return FALSE;
+}
+
diff --git a/private/nw/rdr/volinfo.c b/private/nw/rdr/volinfo.c
new file mode 100644
index 000000000..9e2477c44
--- /dev/null
+++ b/private/nw/rdr/volinfo.c
@@ -0,0 +1,1278 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileinfo.c
+
+Abstract:
+
+ This module implements the get / set volume information routines for
+ netware redirector.
+
+ Setting volume information is currently unimplemented.
+
+Author:
+
+ Manny Weiser (mannyw) 4-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+#define NW_FS_NAME L"NWCompat"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_VOLINFO)
+
+//
+// Local procedure prototypes.
+//
+
+NTSTATUS
+NwCommonQueryVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NwQueryAttributeInfo (
+ IN PVCB Vcb,
+ IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ );
+
+NTSTATUS
+NwQueryVolumeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ );
+
+NTSTATUS
+NwQueryLabelInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_LABEL_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ );
+
+NTSTATUS
+NwQuerySizeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length
+ );
+
+NTSTATUS
+QueryFsSizeInfoCallback(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+QueryFsSizeInfoCallback2(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+NwQueryDeviceInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_DEVICE_INFORMATION Buffer,
+ IN ULONG Length
+ );
+
+NTSTATUS
+NwCommonSetVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdQueryVolumeInformation )
+#pragma alloc_text( PAGE, NwCommonQueryVolumeInformation )
+#pragma alloc_text( PAGE, NwQueryAttributeInfo )
+#pragma alloc_text( PAGE, NwQueryVolumeInfo )
+#pragma alloc_text( PAGE, NwQueryLabelInfo )
+#pragma alloc_text( PAGE, NwQuerySizeInfo )
+#pragma alloc_text( PAGE, NwQueryDeviceInfo )
+#pragma alloc_text( PAGE, NwFsdSetVolumeInformation )
+#pragma alloc_text( PAGE, NwCommonSetVolumeInformation )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, QueryFsSizeInfoCallback )
+#pragma alloc_text( PAGE1, QueryFsSizeInfoCallback2 )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdQueryVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtQueryVolumeInformationFile
+ API calls.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies a pointer to the device object to use.
+
+ Irp - Supplies a pointer to the Irp to process.
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdQueryVolumeInformation\n", 0);
+
+ //
+ // Call the common query volume information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonQueryVolumeInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdQueryVolumeInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonQueryVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for querying volume information.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ ULONG length;
+ ULONG bytesWritten;
+ FS_INFORMATION_CLASS fsInformationClass;
+ PVOID buffer;
+
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PVOID fsContext, fsContext2;
+ PICB icb = NULL;
+ PVCB vcb = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonQueryInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
+ DebugTrace( 0, Dbg, " ->FsInformationClass = %08lx\n", irpSp->Parameters.QueryVolume.FsInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Find out who are.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "Handle is closing\n", 0);
+
+ NwCompleteRequest( pIrpContext, STATUS_INVALID_HANDLE );
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonQueryVolumeInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Decide how to handle this request. A user can query information
+ // on a VCB only.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_RCB:
+ break;
+
+ case NW_NTC_ICB:
+ icb = (PICB)fsContext2;
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ vcb = icb->SuperType.Fcb->Vcb;
+
+ pIrpContext->pNpScb = icb->SuperType.Fcb->Scb->pNpScb;
+
+ break;
+
+ default: // This is not a nodetype
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+ DebugTrace(-1, Dbg, "NwCommonQueryVolumeInformation -> STATUS_INVALID_PARAMETER\n", 0);
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ length = irpSp->Parameters.QueryVolume.Length;
+ fsInformationClass = irpSp->Parameters.QueryVolume.FsInformationClass;
+ buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ try {
+
+ //
+ // Decide how to handle the request.
+ //
+
+ switch (fsInformationClass) {
+
+ case FileFsVolumeInformation:
+
+ status = NwQueryVolumeInfo( pIrpContext, vcb, buffer, length, &bytesWritten );
+ break;
+
+ case FileFsLabelInformation:
+ status = NwQueryLabelInfo( pIrpContext, vcb, buffer, length, &bytesWritten );
+ break;
+
+ case FileFsSizeInformation:
+ if ( vcb != NULL ) {
+ status = NwQuerySizeInfo( pIrpContext, vcb, buffer, length );
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ case FileFsDeviceInformation:
+ status = NwQueryDeviceInfo( pIrpContext, vcb, buffer, length );
+ bytesWritten = sizeof( FILE_FS_DEVICE_INFORMATION );
+ break;
+
+ case FileFsAttributeInformation:
+
+ if ( vcb != NULL ) {
+ status = NwQueryAttributeInfo( vcb, buffer, length, &bytesWritten );
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ DebugTrace(0, Dbg, "Unhandled query volume level %d\n", fsInformationClass );
+ break;
+ }
+
+ //
+ // Set the information field to the number of bytes actually
+ // filled in and then complete the request.
+ //
+ // If the worker function returned status pending, it's
+ // callback routine will fill the information field.
+ //
+
+ if ( status != STATUS_PENDING ) {
+ Irp->IoStatus.Information = bytesWritten;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonQueryVolumeInformation -> %08lx\n", status );
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryAttributeInfo (
+ IN PVCB Vcb,
+ IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs attribute information operation.
+
+Arguments:
+
+ Vcb - Supplies the VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+ BytesWritten - Returns the number of bytes written to the buffer.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG bytesToCopy;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryFsAttributeInfo...\n", 0);
+
+ //
+ // See how many bytes of the file system name we can copy.
+ //
+
+ Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0] );
+
+ *BytesWritten = FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0] );
+
+ if ( Length >= sizeof(NW_FS_NAME) - 2 ) {
+
+ status = STATUS_SUCCESS;
+ *BytesWritten += sizeof(NW_FS_NAME - 2);
+ bytesToCopy = sizeof( NW_FS_NAME - 2 );
+
+ } else {
+
+ status = STATUS_BUFFER_OVERFLOW;
+ *BytesWritten += Length;
+ bytesToCopy = Length;
+ }
+
+ //
+ // Fill in the attribute information.
+ //
+
+ Buffer->FileSystemAttributes = 0;
+
+ if ( Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ) {
+ Buffer->MaximumComponentNameLength = 12;
+ } else {
+ Buffer->MaximumComponentNameLength = NW_MAX_FILENAME_LENGTH;
+ }
+
+ //
+ // And copy over the file name and its length.
+ //
+
+ RtlMoveMemory( &Buffer->FileSystemName[0],
+ NW_FS_NAME,
+ bytesToCopy );
+
+ Buffer->FileSystemNameLength = bytesToCopy;
+
+ return status;
+}
+
+
+
+NTSTATUS
+NwQueryVolumeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs volume information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING VolumeName;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryVolumeInfo...\n", 0);
+
+ //
+ // Do the volume request synchronously.
+ //
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_STATS,
+ Vcb->Specific.Disk.Handle );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ VolumeName.MaximumLength =
+ MIN( MAX_VOLUME_NAME_LENGTH * sizeof( WCHAR ),
+ Length - FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
+ VolumeName.Buffer = Buffer->VolumeLabel;
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N=====R",
+ &VolumeName,
+ MAX_VOLUME_NAME_LENGTH );
+
+ //
+ // Fill in the volume information.
+ //
+
+ Buffer->VolumeCreationTime.HighPart = 0;
+ Buffer->VolumeCreationTime.LowPart = 0;
+ Buffer->VolumeSerialNumber = 0;
+ Buffer->VolumeLabelLength = VolumeName.Length;
+ Buffer->SupportsObjects = FALSE;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel[0] ) +
+ VolumeName.Length;
+ *BytesWritten = pIrpContext->pOriginalIrp->IoStatus.Information;
+
+ pIrpContext->pOriginalIrp->IoStatus.Status = status;
+
+ //
+ // If the volume has been unmounted and remounted then we will
+ // fail this dir but the next one will be fine.
+ //
+
+ if (status == STATUS_UNSUCCESSFUL) {
+ NwReopenVcbHandle( pIrpContext, Vcb);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryLabelInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_LABEL_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs label information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING VolumeName;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryLabelInfo...\n", 0);
+
+ //
+ // Do the volume query synchronously.
+ //
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_STATS,
+ Vcb->Specific.Disk.Handle );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ VolumeName.MaximumLength =
+ MIN( MAX_VOLUME_NAME_LENGTH * sizeof( WCHAR ),
+ Length - FIELD_OFFSET(FILE_FS_LABEL_INFORMATION, VolumeLabel ) );
+ VolumeName.Buffer = Buffer->VolumeLabel;
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N=====R",
+ &VolumeName, 12 );
+
+ //
+ // Fill in the label information.
+ //
+
+ Buffer->VolumeLabelLength = VolumeName.Length;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ FIELD_OFFSET( FILE_FS_LABEL_INFORMATION, VolumeLabel[0] ) +
+ VolumeName.Length;
+ *BytesWritten = pIrpContext->pOriginalIrp->IoStatus.Information;
+
+ pIrpContext->pOriginalIrp->IoStatus.Status = status;
+
+ return status;
+
+}
+
+
+NTSTATUS
+NwQuerySizeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs size information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryFsSizeInfo...\n", 0);
+
+ //
+ // Remember where the response goes.
+ //
+
+ pIrpContext->Specific.QueryVolumeInformation.Buffer = Buffer;
+ pIrpContext->Specific.QueryVolumeInformation.Length = Length;
+ pIrpContext->Specific.QueryVolumeInformation.VolumeNumber = Vcb->Specific.Disk.VolumeNumber;
+
+ //
+ // Start a Get Size Information NCP
+ //
+
+ status = Exchange(
+ pIrpContext,
+ QueryFsSizeInfoCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_STATS,
+ Vcb->Specific.Disk.Handle );
+
+ return( status );
+}
+
+NTSTATUS
+QueryFsSizeInfoCallback(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the query volume size response and generates
+ a Query Standard Information response.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ PFILE_FS_SIZE_INFORMATION Buffer;
+ NTSTATUS Status;
+
+ DebugTrace(0, Dbg, "QueryFsSizeInfoCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ Buffer = pIrpContext->Specific.QueryVolumeInformation.Buffer;
+ RtlZeroMemory( Buffer, sizeof( FILE_FS_SIZE_INFORMATION ) );
+
+ Status = ParseResponse(
+ pIrpContext,
+ Response,
+ BytesAvailable,
+ "Nwww",
+ &Buffer->SectorsPerAllocationUnit,
+ &Buffer->TotalAllocationUnits.LowPart,
+ &Buffer->AvailableAllocationUnits.LowPart );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if (Buffer->TotalAllocationUnits.LowPart == 0xffff) {
+
+ //
+ // The next callback will fill in all the appropriate size info.
+ //
+
+ Status = Exchange(
+ pIrpContext,
+ QueryFsSizeInfoCallback2,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_INFO,
+ pIrpContext->Specific.QueryVolumeInformation.VolumeNumber );
+
+ if (Status == STATUS_PENDING) {
+ return( STATUS_SUCCESS );
+ }
+
+ } else {
+
+ //
+ // Fill in the remaining size information.
+ //
+
+ Buffer->BytesPerSector = 512;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ sizeof( FILE_FS_SIZE_INFORMATION );
+ }
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, Status );
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+QueryFsSizeInfoCallback2(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the query volume size response and generates
+ a Query Standard Information response.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ PFILE_FS_SIZE_INFORMATION Buffer;
+ NTSTATUS Status;
+ ULONG PurgeableAllocationUnits;
+ ULONG OriginalFreeSpace, OriginalSectorsPerAllocUnit, OriginalTotalSpace;
+ ULONG ScaleSectorsPerUnit;
+
+ DebugTrace(0, Dbg, "QueryFsSizeInfoCallback2...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response. Save off the data from
+ // the GET_VOLUME_STATS call to compute the correct sizes.
+ //
+
+ Buffer = pIrpContext->Specific.QueryVolumeInformation.Buffer;
+
+ OriginalTotalSpace = Buffer->TotalAllocationUnits.LowPart;
+ OriginalFreeSpace = Buffer->AvailableAllocationUnits.LowPart;
+ OriginalSectorsPerAllocUnit = Buffer->SectorsPerAllocationUnit;
+
+ RtlZeroMemory( Buffer, sizeof( FILE_FS_SIZE_INFORMATION ) );
+
+ Status = ParseResponse(
+ pIrpContext,
+ Response,
+ BytesAvailable,
+ "Neee_b",
+ &Buffer->TotalAllocationUnits.LowPart,
+ &Buffer->AvailableAllocationUnits.LowPart,
+ &PurgeableAllocationUnits,
+ 16,
+ &Buffer->SectorsPerAllocationUnit);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // If the original free space was maxed out, just add the
+ // additionally indicated units. Otherwise, return the
+ // original free space (which is the correct limit) and
+ // adjust the sectors per allocation units if necessary.
+ //
+
+ if ( OriginalFreeSpace != 0xffff ) {
+
+ Buffer->AvailableAllocationUnits.LowPart = OriginalFreeSpace;
+
+ if ( ( Buffer->SectorsPerAllocationUnit != 0 ) &&
+ ( OriginalSectorsPerAllocUnit != 0 ) ) {
+
+ //
+ // ScaleSectorsPerUnit should always be a whole number.
+ // There's no floating point here!!
+ //
+
+ if ( (ULONG) Buffer->SectorsPerAllocationUnit <= OriginalSectorsPerAllocUnit ) {
+
+ ScaleSectorsPerUnit =
+ OriginalSectorsPerAllocUnit / Buffer->SectorsPerAllocationUnit;
+ Buffer->TotalAllocationUnits.LowPart /= ScaleSectorsPerUnit;
+
+ } else {
+
+ ScaleSectorsPerUnit =
+ Buffer->SectorsPerAllocationUnit / OriginalSectorsPerAllocUnit;
+ Buffer->TotalAllocationUnits.LowPart *= ScaleSectorsPerUnit;
+ }
+
+ Buffer->SectorsPerAllocationUnit = OriginalSectorsPerAllocUnit;
+ }
+
+ } else {
+
+ Buffer->AvailableAllocationUnits.QuadPart += PurgeableAllocationUnits;
+ }
+
+ } else {
+
+ //
+ // If we didn't succeed the second packet, restore the original values.
+ //
+
+ Buffer->TotalAllocationUnits.LowPart = OriginalTotalSpace;
+ Buffer->AvailableAllocationUnits.LowPart = OriginalFreeSpace;
+ Buffer->SectorsPerAllocationUnit = OriginalSectorsPerAllocUnit;
+
+ }
+
+ //
+ // Fill in the remaining size information.
+ //
+
+ Buffer->BytesPerSector = 512;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ sizeof( FILE_FS_SIZE_INFORMATION );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, Status );
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+NwQueryDeviceInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_DEVICE_INFORMATION Buffer,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs size information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryFsDeviceInfo...\n", 0);
+
+ //
+ // BUGBUG. Is this universally true?
+ //
+
+ Buffer->DeviceType = FILE_DEVICE_DISK;
+ Buffer->Characteristics = FILE_REMOTE_DEVICE;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwFsdSetVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtSetVolumeInformationFile
+ API calls.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies a pointer to the device object to use.
+
+ Irp - Supplies a pointer to the Irp to process.
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdSetVolumeInformation\n", 0);
+
+ //
+ // Call the common query volume information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonSetVolumeInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdSetVolumeInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonSetVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This is the common routine for setting volume information.
+
+Arguments:
+
+ IrpContext - Supplies the Irp context to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ FS_INFORMATION_CLASS fsInformationClass;
+
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PVOID fsContext, fsContext2;
+ PICB icb = NULL;
+ PVCB vcb = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonSetVolumeInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
+ DebugTrace( 0, Dbg, " ->FsInformationClass = %08lx\n", irpSp->Parameters.QueryVolume.FsInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Find out who are.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "Handle is closing\n", 0);
+
+ NwCompleteRequest( pIrpContext, STATUS_INVALID_HANDLE );
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonSetVolumeInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Decide how to handle this request. A user can set information
+ // on a VCB only.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_RCB:
+ break;
+
+ case NW_NTC_ICB:
+ icb = (PICB)fsContext2;
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ vcb = icb->SuperType.Fcb->Vcb;
+
+ pIrpContext->pNpScb = icb->SuperType.Fcb->Scb->pNpScb;
+
+ break;
+
+ default: // This is not a nodetype
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+ DebugTrace(-1, Dbg, "NwCommonSetVolumeInformation -> STATUS_INVALID_PARAMETER\n", 0);
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fsInformationClass = irpSp->Parameters.SetVolume.FsInformationClass;
+
+ try {
+
+ //
+ // Decide how to handle the request.
+ //
+
+ switch (fsInformationClass) {
+
+ case FileFsLabelInformation:
+
+ //
+ // We're not allowed to set the label on a Netware volume.
+ //
+
+ status = STATUS_ACCESS_DENIED;
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ DebugTrace(0, Dbg, "Unhandled set volume level %d\n", fsInformationClass );
+ break;
+ }
+
+ //
+ // Set the information field to the number of bytes actually
+ // filled in and then complete the request.
+ //
+ // If the worker function returned status pending, it's
+ // callback routine will fill the information field.
+ //
+
+ if ( status != STATUS_PENDING ) {
+ Irp->IoStatus.Information = 0;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonSetVolumeInformation -> %08lx\n", status );
+ }
+
+ return status;
+}
+
diff --git a/private/nw/rdr/workque.c b/private/nw/rdr/workque.c
new file mode 100644
index 000000000..aba699e32
--- /dev/null
+++ b/private/nw/rdr/workque.c
@@ -0,0 +1,829 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Workque.c
+
+Abstract:
+
+ This module implements the queue of work from the FSD to the
+ FSP threads (system worker threads) for the NetWare redirector.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+LIST_ENTRY IrpContextList;
+KSPIN_LOCK IrpContextInterlock;
+KSPIN_LOCK ContextInterlock;
+
+LONG FreeContextCount = 4; // Allow up to 4 free contexts
+
+LIST_ENTRY MiniIrpContextList;
+LONG FreeMiniContextCount = 20; // Allow up to 20 free mini contexts
+LONG MiniContextCount = 0; // Allow up to 20 free mini contexts
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WORKQUE)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, InitializeIrpContext )
+#pragma alloc_text( PAGE, UninitializeIrpContext )
+#pragma alloc_text( PAGE, NwAppendToQueueAndWait )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwDequeueIrpContext )
+#pragma alloc_text( PAGE1, AllocateMiniIrpContext )
+#pragma alloc_text( PAGE1, FreeMiniIrpContext )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+AllocateIrpContext
+FreeIrpContext
+NwCompleteRequest
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+PIRP_CONTEXT
+AllocateIrpContext (
+ PIRP pIrp
+ )
+/*++
+
+Routine Description:
+
+ Initialize a work queue structure, allocating all structures used for it.
+
+Arguments:
+
+ pIrp - Supplies the Irp for the applications request
+
+
+Return Value:
+
+ PIRP_CONTEXT - Newly allocated Irp Context.
+
+--*/
+{
+ PIRP_CONTEXT IrpContext;
+
+ if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&IrpContextList, &IrpContextInterlock)) == NULL) {
+
+ try {
+
+ //
+ // If there are no IRP contexts in the "zone", allocate a new
+ // Irp context from non paged pool.
+ //
+
+ IrpContext = ALLOCATE_POOL_EX(NonPagedPool, sizeof(IRP_CONTEXT));
+
+ RtlFillMemory( IrpContext, sizeof(IRP_CONTEXT), 0 );
+
+ IrpContext->TxMdl = NULL;
+ IrpContext->RxMdl = NULL;
+
+ KeInitializeEvent( &IrpContext->Event, SynchronizationEvent, FALSE );
+
+ IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT;
+ IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
+
+ IrpContext->TxMdl = ALLOCATE_MDL( &IrpContext->req, MAX_SEND_DATA, FALSE, FALSE, NULL );
+ if ( IrpContext->TxMdl == NULL) {
+ InternalError(("Could not allocate TxMdl for IRP context\n"));
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ IrpContext->RxMdl = ALLOCATE_MDL( &IrpContext->rsp, MAX_RECV_DATA, FALSE, FALSE, NULL );
+ if ( IrpContext->RxMdl == NULL) {
+ InternalError(("Could not allocate RxMdl for IRP context\n"));
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ } finally {
+
+ if ( AbnormalTermination() ) {
+
+ if ( IrpContext != NULL ) {
+
+ if (IrpContext->TxMdl != NULL ) {
+ FREE_MDL( IrpContext->TxMdl );
+ }
+
+ FREE_POOL( IrpContext );
+ } else {
+ InternalError(("Could not allocate pool for IRP context\n"));
+ }
+ }
+ }
+
+ MmBuildMdlForNonPagedPool(IrpContext->TxMdl);
+ MmBuildMdlForNonPagedPool(IrpContext->RxMdl);
+
+#ifdef NWDBG
+ // Make it easy to find fields in the context
+ IrpContext->Signature1 = 0xfeedf00d;
+ IrpContext->Signature2 = 0xfeedf00d;
+ IrpContext->Signature3 = 0xfeedf00d;
+#endif
+
+ // IrpContext is allocated. Finish off initialization.
+
+ } else {
+
+ // Record that we have removed an entry from the free list
+ ExInterlockedIncrementLong(&FreeContextCount,&ContextInterlock);
+
+ ASSERT( IrpContext != NULL );
+
+ //
+ // The free list uses the start of the structure for the list entry
+ // so restore corrupted fields.
+ //
+
+ IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT;
+ IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
+
+ // Ensure mdl's are clean
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->RxMdl->Next = NULL;
+ IrpContext->RxMdl->ByteCount = MAX_RECV_DATA;
+
+ //
+ // Clean "used" fields
+ //
+
+ IrpContext->Flags = 0;
+ IrpContext->Icb = NULL;
+ IrpContext->pEx = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->ReceiveDataRoutine = NULL;
+ IrpContext->pTdiStruct = NULL;
+
+ //
+ // Clean the specific data zone.
+ //
+
+ RtlZeroMemory( &(IrpContext->Specific), sizeof( IrpContext->Specific ) );
+ }
+
+ ExInterlockedIncrementLong(&ContextCount,&ContextInterlock);
+
+ //
+ // Save away the fields in the Irp that might be tromped by
+ // building the Irp for the exchange with the server.
+ //
+
+ IrpContext->pOriginalIrp = pIrp;
+
+ if ( pIrp != NULL) {
+ IrpContext->pOriginalSystemBuffer = pIrp->AssociatedIrp.SystemBuffer;
+ IrpContext->pOriginalUserBuffer = pIrp->UserBuffer;
+ IrpContext->pOriginalMdlAddress = pIrp->MdlAddress;
+ }
+
+#ifdef NWDBG
+ IrpContext->pNpScb = NULL;
+#endif
+
+ ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
+
+ return IrpContext;
+}
+
+ VOID
+FreeIrpContext (
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ Initialize a work queue structure, allocating all structures used for it.
+
+Arguments:
+
+ PIRP_CONTEXT IrpContext - Irp Context to free.
+ None
+
+
+Return Value:
+
+
+--*/
+{
+
+ ASSERT( IrpContext->NodeTypeCode == NW_NTC_IRP_CONTEXT );
+ ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
+ ASSERT( IrpContext->PostProcessRoutine == NULL );
+
+ FreeReceiveIrp( IrpContext );
+
+#ifdef NWDBG
+ IrpContext->DebugValue = 0;
+#endif
+ IrpContext->Flags = 0;
+
+ //
+ // Cleanup the Irp needs to be restored to its original settings.
+ //
+
+ if ( IrpContext->pOriginalIrp != NULL ) {
+
+ PIRP pIrp = IrpContext->pOriginalIrp;
+
+ pIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
+
+ pIrp->UserBuffer = IrpContext->pOriginalUserBuffer;
+
+ pIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
+
+#ifdef NWDBG
+ IrpContext->pOriginalIrp = NULL;
+#endif
+ }
+
+#ifdef NWDBG
+ RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ) );
+#endif
+
+ ExInterlockedDecrementLong(&ContextCount, &ContextInterlock);
+
+ if ( ExInterlockedDecrementLong(&FreeContextCount, &ContextInterlock) !=
+ ResultNegative ) {
+
+ //
+ // We use the first two longwords of the IRP context as a list entry
+ // when we free it to the list.
+ //
+
+ ExInterlockedInsertTailList(&IrpContextList,
+ (PLIST_ENTRY )IrpContext,
+ &IrpContextInterlock);
+ } else {
+ //
+ // We already have as many free context as we allow so destroy
+ // this context. Restore FreeContextCount to its original value.
+ //
+
+ ExInterlockedIncrementLong( &FreeContextCount, &ContextInterlock );
+
+ FREE_MDL( IrpContext->TxMdl );
+ FREE_MDL( IrpContext->RxMdl );
+ FREE_POOL(IrpContext);
+#ifdef NWDBG
+ ContextCount --;
+#endif
+ }
+}
+
+
+ VOID
+InitializeIrpContext (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize the Irp Context system
+
+Arguments:
+
+ None.
+
+
+Return Value:
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ KeInitializeSpinLock(&IrpContextInterlock);
+ KeInitializeSpinLock(&ContextInterlock);
+ InitializeListHead(&IrpContextList);
+ InitializeListHead(&MiniIrpContextList);
+}
+
+ VOID
+UninitializeIrpContext (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize the Irp Context system
+
+Arguments:
+
+ None.
+
+
+Return Value:
+ None.
+
+--*/
+{
+ PIRP_CONTEXT IrpContext;
+ PLIST_ENTRY ListEntry;
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ PAGED_CODE();
+
+ //
+ // Free all the IRP contexts.
+ //
+
+ while ( !IsListEmpty( &IrpContextList ) ) {
+ IrpContext = (PIRP_CONTEXT)RemoveHeadList( &IrpContextList );
+
+ FREE_MDL( IrpContext->TxMdl );
+ FREE_MDL( IrpContext->RxMdl );
+ FREE_POOL(IrpContext);
+ }
+
+ while ( !IsListEmpty( &MiniIrpContextList ) ) {
+
+ ListEntry = RemoveHeadList( &MiniIrpContextList );
+ MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
+
+ FREE_POOL( MiniIrpContext->Buffer );
+ FREE_MDL( MiniIrpContext->Mdl2 );
+ FREE_MDL( MiniIrpContext->Mdl1 );
+ FREE_IRP( MiniIrpContext->Irp );
+ FREE_POOL( MiniIrpContext );
+ }
+}
+
+
+VOID
+NwCompleteRequest (
+ PIRP_CONTEXT IrpContext,
+ NTSTATUS Status
+ )
+/*++
+
+Routine Description:
+
+ The following procedure is used by the FSP and FSD routines to complete
+ an IRP.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context information.
+
+ Status - The status to use to complete the IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP Irp;
+
+ if ( IrpContext == NULL ) {
+ return;
+ }
+
+ if ( Status == STATUS_PENDING ) {
+ return;
+ }
+
+ if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
+ Error( EVENT_NWRDR_RESOURCE_SHORTAGE, Status, NULL, 0, 0 );
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+
+ Irp->IoStatus.Status = Status;
+ DebugTrace(0, Dbg, "Completing Irp with status %X\n", Status );
+
+ // Restore the Irp to its original state
+
+ FreeIrpContext( IrpContext );
+
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ return;
+}
+
+
+VOID
+NwAppendToQueueAndWait(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine appends an IrpContext to the SCB queue, and waits the
+ the queue to be ready to process the Irp.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context information.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BOOLEAN AtFront;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwAppendToQueueAndWait\n", 0);
+
+ IrpContext->RunRoutine = SetEvent;
+
+#ifdef MSWDBG
+ ASSERT( IrpContext->Event.Header.SignalState == 0 );
+#endif
+
+ AtFront = AppendToScbQueue( IrpContext, IrpContext->pNpScb );
+
+ if ( AtFront ) {
+ KickQueue( IrpContext->pNpScb );
+ }
+
+ //
+ // Wait until we get to the front of the queue.
+ //
+
+ KeWaitForSingleObject(
+ &IrpContext->Event,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ DebugTrace(-1, Dbg, "NwAppendToQueueAndWait\n", 0);
+ return;
+}
+
+
+VOID
+NwDequeueIrpContext(
+ IN PIRP_CONTEXT pIrpContext,
+ IN BOOLEAN OwnSpinLock
+ )
+/*++
+
+Routine Description:
+
+ This routine removes an IRP Context from the front the SCB queue.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context information.
+
+ OwnSpinLock - If TRUE, the caller owns the SCB spin lock.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY pListEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNpScb;
+
+ DebugTrace(+1, Dbg, "NwDequeueIrpContext\n", 0);
+
+ if (!BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) {
+ DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0);
+ return;
+ }
+
+ pNpScb = pIrpContext->pNpScb;
+
+ if ( !OwnSpinLock ) {
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+ }
+
+ //
+ // Disable timer from looking at this queue.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+
+ pListEntry = RemoveHeadList( &pNpScb->Requests );
+
+ if ( !OwnSpinLock ) {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+#ifdef NWDBG
+ ASSERT ( CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) == pIrpContext );
+
+ {
+
+ PIRP_CONTEXT RemovedContext = CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest );
+ if ( RemovedContext != pIrpContext ) {
+ DbgBreakPoint();
+ }
+
+ }
+
+ DebugTrace(
+ 0,
+ Dbg,
+ "Dequeued IRP Context %08lx\n",
+ CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) );
+
+#ifdef MSWDBG
+ pNpScb->RequestDequeued = TRUE;
+#endif
+
+#endif
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ //
+ // Give the next IRP context on the SCB queue a chance to run.
+ //
+
+ KickQueue( pNpScb );
+
+ DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0);
+ return;
+}
+
+
+VOID
+NwCancelIrp (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the cancel function for an IRP being processed
+ by the redirector.
+
+Arguments:
+
+ DeviceObject - ignored
+
+ Irp - Supplies the Irp being cancelled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY listEntry, nextListEntry;
+ KIRQL OldIrql;
+ PIRP_CONTEXT pTestIrpContext;
+ PIRP pTestIrp;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // We now need to void the cancel routine and release the io cancel
+ // spin-lock.
+ //
+
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ //
+ // Now we have to search for the IRP to cancel everywhere. So just
+ // look for cancelled IRPs and process them all.
+ //
+
+ //
+ // Process the Get Message queue.
+ //
+
+ KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql );
+
+ for ( listEntry = NwGetMessageList.Flink;
+ listEntry != &NwGetMessageList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ //
+ // If the file object of the queued request, matches the file object
+ // that is being closed, remove the IRP from the queue, and
+ // complete it with an error.
+ //
+
+ pTestIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+ pTestIrp = pTestIrpContext->pOriginalIrp;
+
+ if ( pTestIrp->Cancel ) {
+ RemoveEntryList( listEntry );
+ NwCompleteRequest( pTestIrpContext, STATUS_CANCELLED );
+ }
+
+ }
+
+ KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
+
+ //
+ // Process the set of SCB IRP queues.
+ //
+
+ // BUGBUG. Not done yet. Needed?
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+PMINI_IRP_CONTEXT
+AllocateMiniIrpContext (
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an IRP, a buffer, and an MDL for sending
+ a burst write fragment.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Irp - The allocated and initialized IRP.
+ NULL - The IRP allocation failed.
+
+--*/
+
+{
+ PMINI_IRP_CONTEXT MiniIrpContext;
+ PIRP Irp = NULL;
+ PMDL Mdl1 = NULL, Mdl2 = NULL;
+ PVOID Buffer = NULL;
+ PLIST_ENTRY ListEntry;
+
+ ListEntry = ExInterlockedRemoveHeadList(
+ &MiniIrpContextList,
+ &IrpContextInterlock);
+
+ if ( ListEntry == NULL) {
+
+ try {
+ MiniIrpContext = ALLOCATE_POOL_EX( NonPagedPool, sizeof( *MiniIrpContext ) );
+
+ MiniIrpContext->NodeTypeCode = NW_NTC_MINI_IRP_CONTEXT;
+ MiniIrpContext->NodeByteSize = sizeof( *MiniIrpContext );
+
+ Irp = ALLOCATE_IRP(
+ IrpContext->pNpScb->Server.pDeviceObject->StackSize,
+ FALSE );
+
+ if ( Irp == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Buffer = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NCP_BURST_HEADER ) );
+
+ Mdl1 = ALLOCATE_MDL( Buffer, sizeof( NCP_BURST_HEADER ), FALSE, FALSE, NULL );
+ if ( Mdl1 == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( Mdl1 );
+
+ //
+ // Since this MDL can be used to send a packet on any server,
+ // allocate an MDL large enough for any packet size.
+ //
+
+ Mdl2 = ALLOCATE_MDL( 0, 65535 + PAGE_SIZE - 1, FALSE, FALSE, NULL );
+ if ( Mdl2 == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Mdl1->Next = Mdl2;
+
+ MiniIrpContext->Irp = Irp;
+ MiniIrpContext->Buffer = Buffer;
+ MiniIrpContext->Mdl1 = Mdl1;
+ MiniIrpContext->Mdl2 = Mdl2;
+
+ ExInterlockedIncrementLong( &MiniContextCount, &ContextInterlock );
+
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( Buffer != NULL ) {
+ FREE_POOL( Buffer );
+ }
+
+ if ( Irp != NULL ) {
+ FREE_IRP( Irp );
+ }
+
+ if ( Mdl1 != NULL ) {
+ FREE_MDL( Mdl1 );
+ }
+
+ return( NULL );
+ }
+
+ } else {
+
+ //
+ // Record that we have removed an entry from the free list.
+ //
+
+ ExInterlockedIncrementLong( &FreeMiniContextCount, &ContextInterlock );
+ MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
+
+ }
+
+ MiniIrpContext->IrpContext = IrpContext;
+
+ return( MiniIrpContext );
+}
+
+VOID
+FreeMiniIrpContext (
+ PMINI_IRP_CONTEXT MiniIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees a mini IRP Context.
+
+Arguments:
+
+ MiniIrpContext - The mini IRP context to free.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ExInterlockedDecrementLong( &MiniContextCount, &ContextInterlock );
+
+ if ( ExInterlockedDecrementLong( &FreeMiniContextCount, &ContextInterlock) !=
+ ResultNegative ) {
+
+ //
+ // Ok to keep this mini irp context. Just queue it to the free list.
+ //
+
+ MmPrepareMdlForReuse( MiniIrpContext->Mdl2 );
+
+ ExInterlockedInsertTailList(
+ &MiniIrpContextList,
+ &MiniIrpContext->Next,
+ &IrpContextInterlock );
+
+ } else {
+
+ //
+ // We already have as many free context as we allow so destroy
+ // this context. Restore FreeContextCount to its original value.
+ //
+
+ ExInterlockedIncrementLong( &FreeContextCount, &ContextInterlock );
+
+ FREE_POOL( MiniIrpContext->Buffer );
+ FREE_MDL( MiniIrpContext->Mdl2 );
+ FREE_MDL( MiniIrpContext->Mdl1 );
+ FREE_IRP( MiniIrpContext->Irp );
+
+ FREE_POOL( MiniIrpContext );
+ }
+}
+
diff --git a/private/nw/rdr/write.c b/private/nw/rdr/write.c
new file mode 100644
index 000000000..60d6a2cd5
--- /dev/null
+++ b/private/nw/rdr/write.c
@@ -0,0 +1,3063 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Write.c
+
+Abstract:
+
+ This module implements support for NtWriteFile for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 07-Apr-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h>
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WRITE)
+
+//
+// The header overhead in the first packet of a burst write.
+//
+
+#define BURST_WRITE_HEADER_SIZE \
+ ( sizeof( NCP_BURST_WRITE_REQUEST ) - sizeof( NCP_BURST_HEADER ) )
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonWrite (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteNcp(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+QueryEofForWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+WriteNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+BurstWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+SendWriteBurst(
+ PIRP_CONTEXT IrpContext,
+ ULONG Offset,
+ USHORT Length,
+ BOOLEAN EndOfBurst,
+ BOOLEAN Retransmission
+ );
+
+VOID
+BuildBurstWriteFirstReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ PMDL BurstMdl,
+ UCHAR Flags,
+ ULONG Handle,
+ ULONG FileOffset
+ );
+
+VOID
+BuildBurstWriteNextReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ UCHAR BurstFlags,
+ ULONG BurstOffset,
+ PMDL BurstHeaderMdl,
+ PMDL BurstDataMdl
+ );
+
+NTSTATUS
+BurstWriteCompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+BurstWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+BurstWriteTimeout(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BurstWriteReconnect(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwCommonFlushBuffers (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+FlushBuffersCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+SendSecondaryPacket(
+ PIRP_CONTEXT IrpContext,
+ PIRP Irp
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdWrite )
+#pragma alloc_text( PAGE, NwCommonWrite )
+#pragma alloc_text( PAGE, DoWrite )
+#pragma alloc_text( PAGE, WriteNcp )
+#pragma alloc_text( PAGE, BurstWrite )
+#pragma alloc_text( PAGE, SendWriteBurst )
+#pragma alloc_text( PAGE, ResubmitBurstWrite )
+#pragma alloc_text( PAGE, NwFsdFlushBuffers )
+#pragma alloc_text( PAGE, NwCommonFlushBuffers )
+#pragma alloc_text( PAGE, BuildBurstWriteFirstReq )
+#pragma alloc_text( PAGE, BuildBurstWriteNextReq )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, WriteNcpCallback )
+#pragma alloc_text( PAGE1, BurstWriteCompletionSend )
+#pragma alloc_text( PAGE1, BurstWriteCallback )
+#pragma alloc_text( PAGE1, BurstWriteTimeout )
+#pragma alloc_text( PAGE1, FlushBuffersCallback )
+#pragma alloc_text( PAGE1, SendSecondaryPacket )
+#pragma alloc_text( PAGE1, BurstWriteReconnect )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtWriteFile.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the write function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdWrite\n", 0);
+
+ //
+ // Call the common write routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonWrite( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdWrite -> %08lx\n", status );
+
+ Stats.WriteOperations++;
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonWrite (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtWriteFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PNONPAGED_FCB pNpFcb;
+ PVOID fsContext;
+
+ BOOLEAN WroteToCache;
+ LARGE_INTEGER ByteOffset;
+ LARGE_INTEGER PreviousByteOffset;
+ ULONG BufferLength;
+
+ PULONG pFileSize;
+
+ // ULONG FileLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonWrite...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+
+ if (((nodeTypeCode != NW_NTC_ICB) &&
+ (nodeTypeCode != NW_NTC_ICB_SCB)) ||
+ (!icb->HasRemoteHandle) ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( icb );
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ pFileSize = &icb->NpFcb->Header.FileSize.LowPart;
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ IrpContext->pScb = icb->SuperType.Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ fcb = NULL;
+ pFileSize = &icb->FileSize;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status );
+ return status;
+ }
+
+ ByteOffset = irpSp->Parameters.Write.ByteOffset;
+ BufferLength = irpSp->Parameters.Write.Length;
+
+ //
+ // Can't handle large byte offset, but write to EOF is okay.
+ //
+
+ if ( ByteOffset.HighPart != 0 ) {
+
+ if ( ByteOffset.HighPart != 0xFFFFFFFF ||
+ ByteOffset.LowPart != 0xFFFFFFFF ) {
+
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ PreviousByteOffset.QuadPart = irpSp->FileObject->CurrentByteOffset.QuadPart;
+ irpSp->FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart;
+ }
+
+ //
+ // Paging I/O is not allowed to extend the file
+ //
+
+ if ((FlagOn(Irp->Flags, IRP_PAGING_IO)) &&
+ (ByteOffset.LowPart + BufferLength > *pFileSize )) {
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( ByteOffset.LowPart + BufferLength <= *pFileSize ) {
+
+ //
+ // Someone else extended the file. Do nothing.
+ //
+
+ // continue;
+
+ } else if ( ByteOffset.LowPart > *pFileSize ) {
+
+ //
+ // Whole write is off the end of the buffer
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+
+ } else {
+
+ //
+ // Truncate request to size of file
+ //
+
+ BufferLength = *pFileSize - ByteOffset.LowPart;
+
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+
+ //
+ // Special case 0 length write.
+ //
+
+ if ( BufferLength == 0 ) {
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Remember the original MDL, so that we can restore it when we are done.
+ //
+
+ IrpContext->pOriginalMdlAddress = Irp->MdlAddress;
+
+ //
+ // Attempt to write this data to our private cache
+ //
+ // BUGBUG - Cheap fix, don't process MDL based writes. Fix up
+ // post Daytona beta.
+ //
+
+ if ( fcb != NULL && Irp->UserBuffer != NULL ) {
+
+ WroteToCache = CacheWrite(
+ IrpContext,
+ fcb->NonPagedFcb,
+ ByteOffset.LowPart,
+ BufferLength,
+ Irp->UserBuffer );
+
+ if ( WroteToCache ) {
+
+ Irp->IoStatus.Information = BufferLength;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BufferLength;
+ }
+
+ //
+ // Record write offset and size to discover a sequential write pattern.
+ //
+
+ fcb->LastReadOffset = irpSp->Parameters.Write.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Write.Length;
+
+ //
+ // If the file was extended, record the new file size.
+ //
+
+ if ( fcb->LastReadOffset + fcb->LastReadSize >
+ fcb->NonPagedFcb->Header.FileSize.LowPart ) {
+
+ fcb->NonPagedFcb->Header.FileSize.LowPart =
+ fcb->LastReadOffset + fcb->LastReadSize;
+ }
+
+ DebugTrace(-1, Dbg, "NwCommonWrite -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ }
+
+ status = DoWrite(
+ IrpContext,
+ ByteOffset,
+ BufferLength,
+ Irp->UserBuffer,
+ IrpContext->pOriginalMdlAddress );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ //
+ // We actually wrote something out to the wire. If there was a read
+ // cache and this write overlapped it, invalidate the read cache data
+ // so that we get good data on future reads.
+ //
+
+ if ( fcb != NULL ) {
+
+ pNpFcb = fcb->NonPagedFcb;
+
+ if ( ( pNpFcb->CacheBuffer != NULL ) &&
+ ( pNpFcb->CacheSize != 0 ) &&
+ ( pNpFcb->CacheType == ReadAhead ) ) {
+
+ //
+ // Two cases: (1) offset is less than cache offset
+ // (2) offset is inside cached region
+ //
+
+ if ( ByteOffset.LowPart < pNpFcb->CacheFileOffset ) {
+
+ //
+ // Did we run into the read cache?
+ //
+
+ if ( BufferLength >
+ (pNpFcb->CacheFileOffset - ByteOffset.LowPart) ) {
+
+ DebugTrace( 0, Dbg, "Invalidated read cache for %08lx.\n", pNpFcb );
+ pNpFcb->CacheDataSize = 0;
+
+ }
+
+ } else {
+
+ //
+ // Did we write over any of the cached region.
+ //
+
+ if ( ByteOffset.LowPart <= ( pNpFcb->CacheFileOffset + pNpFcb->CacheDataSize ) ) {
+
+ DebugTrace( 0, Dbg, "Invalidated read cache for %08lx.\n", pNpFcb );
+ pNpFcb->CacheDataSize = 0;
+
+ }
+ }
+ }
+
+ }
+
+ Irp->IoStatus.Information = IrpContext->Specific.Write.WriteOffset;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BufferLength;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if (ByteOffset.LowPart + BufferLength > *pFileSize ) {
+
+ *pFileSize = ByteOffset.LowPart + BufferLength;
+
+ }
+
+ } else {
+
+ //
+ // The request failed, don't move the file pointer.
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart = PreviousByteOffset.QuadPart;
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status);
+
+ return status;
+}
+
+NTSTATUS
+DoWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine does a write to the network via the most efficient
+ available protocol.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ByteOffset - The file offset to write.
+
+ BufferLength - The number of bytes to write.
+
+ WriteBuffer - A pointer to the source buffer.
+
+ WriteMdl = An optional MDL for the write buffer.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if ( IrpContext->pNpScb->SendBurstModeEnabled &&
+ BufferLength > IrpContext->pNpScb->BufferSize ) {
+ status = BurstWrite( IrpContext, ByteOffset, BufferLength, WriteBuffer, WriteMdl );
+ } else {
+ status = WriteNcp( IrpContext, ByteOffset, BufferLength, WriteBuffer, WriteMdl );
+ }
+
+ //
+ // Reset IrpContext parameters
+ //
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->Flags &= ~(IRP_FLAG_RETRY_SEND | IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET |
+ IRP_FLAG_BURST_WRITE | IRP_FLAG_NOT_SYSTEM_PACKET );
+ IrpContext->pTdiStruct = NULL;
+
+ IrpContext->pOriginalIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
+ IrpContext->pOriginalIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
+
+ return( status );
+}
+
+NTSTATUS
+WriteNcp(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of write NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PICB Icb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ ULONG FileLength;
+
+ PSCB pScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PMDL DataMdl;
+ BOOLEAN Done;
+
+ PAGED_CODE();
+
+ Icb = IrpContext->Icb;
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ DebugTrace(+1, Dbg, "WriteNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "WriteLen= %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+
+ if (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB) {
+ pScb = Icb->SuperType.Fcb->Scb;
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ } else {
+
+ //
+ // Write to a queue
+ //
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ if ( ByteOffset.HighPart == 0xFFFFFFFF &&
+ ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE ) {
+
+ //
+ // Write relative to end of file. Find the end of file.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof( Icb->Handle ) );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileLength );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ }
+
+ IrpContext->Specific.Write.FileOffset = FileLength;
+ }
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize, BufferLength );
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ //
+ // The server will not accept writes that cross 4k boundaries in the file
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( ByteOffset.LowPart, Length ))) {
+ Length = 4096 -
+ ((ULONG)ByteOffset.LowPart & (4096-1));
+ }
+
+ IrpContext->Specific.Write.Buffer = WriteBuffer;
+ IrpContext->Specific.Write.WriteOffset = 0;
+ IrpContext->Specific.Write.RemainingLength = BufferLength;
+ IrpContext->Specific.Write.LastWriteLength = Length;
+ IrpContext->Specific.Write.FileOffset = ByteOffset.LowPart;
+ IrpContext->Specific.Write.PartialMdl = NULL;
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Setup to do at most 64K of i/o asynchronously, or buffer length.
+ //
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( 64 * 1024, IrpContext->Specific.Write.RemainingLength );
+ IrpContext->Specific.Write.BurstOffset = 0;
+
+ //
+ // Try to allocate an MDL for this i/o.
+ //
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ IrpContext->Specific.Write.BurstLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ if ( IrpContext->Specific.Write.PartialMdl != NULL ) {
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+ }
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", STATUS_INSUFFICIENT_RESOURCES );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpContext->Specific.Write.FullMdl = DataMdl;
+
+
+ //
+ // If there is no MDL for this write probe the data MDL to
+ // lock it's pages down. Otherwise, use the data MDL as
+ // a partial MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+
+ //
+ // The Probe may cause us to page in some data. If the data is from
+ // the same server we are writing to then we had better not be at
+ // the front of the queue otherwise it will wait indefinitely behind us.
+ // Its a good idea to Dequeue ourselves after each burst anyway because
+ // its a quick operation and it alow smaller requests to overtake a very
+ // large series of bursts.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoReadAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", GetExceptionCode() );
+ return GetExceptionCode();
+ }
+
+ } else {
+ IoBuildPartialMdl(
+ WriteMdl,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Write.Buffer,
+ IrpContext->Specific.Write.BurstLength );
+ }
+
+ //
+ // Allocate a partial Mdl for the worst possible case of alignment
+ //
+
+ IrpContext->Specific.Write.PartialMdl =
+ ALLOCATE_MDL( 0 , IrpContext->pNpScb->BufferSize + PAGE_SIZE-1, FALSE, FALSE, NULL);
+
+ if ( IrpContext->Specific.Write.PartialMdl == NULL ) {
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", STATUS_INSUFFICIENT_RESOURCES );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Build a partial MDL for this write NCP.
+ //
+
+ IoBuildPartialMdl(
+ DataMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ MmGetMdlVirtualAddress( DataMdl ),
+ Length );
+
+ if ( IrpContext->Specific.Write.BurstLength ==
+ IrpContext->Specific.Write.RemainingLength ) {
+ Done = TRUE;
+ }
+
+ //
+ // Send the request.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ WriteNcpCallback,
+ "F-rdwf",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ IrpContext->Specific.Write.FileOffset,
+ Length,
+ IrpContext->Specific.Write.PartialMdl );
+
+ Stats.WriteNcps+=2;
+
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+
+ //
+ // Unlock locked pages, and free our MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ //
+ // If we had a failure, we need to terminate this loop.
+ // The only status that is set is the Specific->Write
+ // status. We can not trust what comes back from the
+ // ExchangeWithWait by design.
+ //
+
+ if ( !NT_SUCCESS( IrpContext->Specific.Write.Status ) ) {
+ Done = TRUE;
+ }
+
+ //
+ // Reset the packet length since we may have less than
+ // a packet to send.
+ //
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Write.RemainingLength );
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ }
+
+ status = IrpContext->Specific.Write.Status;
+
+ DebugTrace(-1, Dbg, "WriteNcp -> %08lx\n", status );
+ return status;
+}
+
+
+NTSTATUS
+WriteNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Length;
+ ULONG LastLength;
+
+ DebugTrace(0, Dbg, "WriteNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ LastLength = IrpContext->Specific.Write.LastWriteLength;
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ // If the last write worked then move the pointers appropriately
+
+ IrpContext->Specific.Write.RemainingLength -= LastLength;
+ IrpContext->Specific.Write.BurstLength -= LastLength;
+ IrpContext->Specific.Write.WriteOffset += LastLength;
+ IrpContext->Specific.Write.FileOffset += LastLength;
+ IrpContext->Specific.Write.BurstOffset += LastLength;
+
+ // If this is a print job, remember that we actually wrote data
+
+ if ( IrpContext->Icb->IsPrintJob ) {
+ IrpContext->Icb->ActuallyPrinted = TRUE;
+ }
+
+ } else {
+
+ //
+ // Abandon this request
+ //
+
+ IrpContext->Specific.Write.Status = Status;
+ NwSetIrpContextEvent( IrpContext );
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return Status;
+ }
+
+
+ if ( IrpContext->Specific.Write.BurstLength != 0 ) {
+
+ // Write the next packet.
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n", IrpContext->Specific.Write.RemainingLength);
+ DebugTrace( 0, Dbg, "FileOffset = %ld\n", IrpContext->Specific.Write.FileOffset);
+ DebugTrace( 0, Dbg, "WriteOffset = %ld\n", IrpContext->Specific.Write.WriteOffset);
+ DebugTrace( 0, Dbg, "BurstOffset = %ld\n", IrpContext->Specific.Write.BurstOffset);
+
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // The server will not accept writes that cross 4k boundaries
+ // in the file.
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( IrpContext->Specific.Write.FileOffset, Length ))) {
+
+ Length = 4096 -
+ ((ULONG)IrpContext->Specific.Write.FileOffset & (4096-1));
+
+ }
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ MmPrepareMdlForReuse( IrpContext->Specific.Write.PartialMdl );
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ IrpContext->Specific.Write.BurstOffset,
+ Length );
+
+ //
+ // Send the request.
+ //
+
+ BuildRequestPacket(
+ IrpContext,
+ WriteNcpCallback,
+ "F-rdwf",
+ NCP_WRITE_FILE,
+ &IrpContext->Icb->Handle, sizeof( IrpContext->Icb->Handle ),
+ IrpContext->Specific.Write.FileOffset,
+ Length,
+ IrpContext->Specific.Write.PartialMdl );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ Stats.WriteNcps+=2;
+
+ DebugTrace(-1, Dbg, "WriteNcbCallBack -> %08lx\n", Status );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Abandon this request
+ //
+
+ IrpContext->Specific.Write.Status = Status;
+ NwSetIrpContextEvent( IrpContext );
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return Status;
+ }
+
+
+ } else {
+
+ //
+ // We're done with this request, signal the writing thread.
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_SUCCESS;
+ NwSetIrpContextEvent( IrpContext );
+ }
+
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+BurstWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of burst write NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of the transfer.
+
+--*/
+{
+ PICB Icb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PMDL DataMdl;
+ BOOLEAN Done;
+ BOOLEAN MissingData;
+
+ ULONG TimeInNwUnits;
+
+ ULONG LastLength;
+ ULONG Result;
+ UCHAR BurstFlags;
+ USHORT MissingFragmentCount;
+ USHORT i;
+ ULONG FragmentOffset;
+ USHORT FragmentLength;
+
+ Icb = IrpContext->Icb;
+ pNpScb = IrpContext->pNpScb;
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ IrpContext->Specific.Write.WriteOffset = 0;
+ IrpContext->Specific.Write.RemainingLength = BufferLength;
+
+ IrpContext->Specific.Write.TotalWriteLength = BufferLength;
+ IrpContext->Specific.Write.TotalWriteOffset = ByteOffset.LowPart;
+
+ DebugTrace(+1, Dbg, "BurstWrite...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "WriteLen= %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+
+ //
+ // Renegotiate burst mode, if necessary
+ //
+
+ if ( pNpScb->BurstRenegotiateReqd ) {
+ pNpScb->BurstRenegotiateReqd = FALSE;
+
+ RenegotiateBurstMode( IrpContext, pNpScb );
+ }
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_WRITE );
+
+ if (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB) {
+
+ pScb = Icb->SuperType.Fcb->Scb;
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+
+ } else {
+
+ //
+ // Write to a queue
+ //
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ //
+ // Calculate the length of the burst to send.
+ //
+
+ Length = MIN( (ULONG)pNpScb->MaxSendSize, BufferLength );
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ if ( ByteOffset.HighPart == 0xFFFFFFFF &&
+ ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE ) {
+
+ ULONG FileLength;
+
+ //
+ // Write relative to end of file. Find the end of file.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof(Icb->Handle) );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileLength );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ IrpContext->Specific.Write.FileOffset = FileLength;
+
+ } else {
+
+ IrpContext->Specific.Write.FileOffset = ByteOffset.LowPart;
+
+ }
+
+ //
+ // Setup context parameters for burst write.
+ //
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+
+ IrpContext->Specific.Write.Buffer = WriteBuffer;
+
+ //
+ // Set the timeout to be the time for all te burst packets to be sent plus a round
+ // trip delay plus a second.
+ //
+
+ TimeInNwUnits = pNpScb->NwSingleBurstPacketTime * ((Length / IrpContext->pNpScb->MaxPacketSize) + 1) +
+ IrpContext->pNpScb->NwLoopTime;
+
+ IrpContext->pNpScb->SendTimeout =
+ (SHORT)(((TimeInNwUnits / 555) *
+ (ULONG)WriteTimeoutMultiplier) / 100 + 1) ;
+
+ if (IrpContext->pNpScb->SendTimeout < 2)
+ {
+ IrpContext->pNpScb->SendTimeout = 2 ;
+ }
+
+ if (IrpContext->pNpScb->SendTimeout > (SHORT)MaxWriteTimeout)
+ {
+ IrpContext->pNpScb->SendTimeout = (SHORT)MaxWriteTimeout ;
+ }
+
+ IrpContext->pNpScb->TimeOut = IrpContext->pNpScb->SendTimeout;
+
+ pNpScb->RetryCount = 20;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->SendTimeout = %08lx\n", IrpContext->pNpScb->SendTimeout );
+
+ Done = FALSE;
+
+ do {
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ Length,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return ( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // If there is no MDL for this write, probe the data MDL to lock it's
+ // pages down.
+ //
+ // Otherwise, use the data MDL as a partial MDL and lock the pages
+ // accordingly.
+ //
+
+ if ( WriteMdl == NULL ) {
+
+ //
+ // The Probe may cause us to page in some data. If the data is from
+ // the same server we are writing to then we had better not be at
+ // the front of the queue otherwise it will wait indefinitely behind us.
+ // Its a good idea to Dequeue ourselves after each burst anyway because
+ // its a quick operation and it alow smaller requests to overtake a very
+ // large series of bursts.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoReadAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ WriteMdl,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ Length );
+ }
+
+ pNpScb->BurstDataWritten += Length;
+
+ if (( SendExtraNcp ) &&
+ ( pNpScb->BurstDataWritten >= 0x0000ffff )) {
+
+
+ ULONG Flags;
+
+ //
+ // VLM client sends an NCP when starting a burst mode request
+ // if the last request was not a write. It also does this every
+ // 0xfe00 bytes written
+ //
+ // When going to a queue we will use handle 2. This is what the vlm
+ // client always seems to do.
+ //
+
+ Flags = IrpContext->Flags;
+
+ //
+ // Reset IrpContext parameters
+ //
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->Flags &= ~(IRP_FLAG_RETRY_SEND | IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET |
+ IRP_FLAG_BURST_WRITE | IRP_FLAG_NOT_SYSTEM_PACKET );
+ IrpContext->pTdiStruct = NULL;
+
+ ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb", // NCP Get Directory Path
+ NCP_DIR_FUNCTION, NCP_GET_DIRECTORY_PATH,
+ (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB)?
+ Icb->SuperType.Fcb->Vcb->Specific.Disk.Handle : 2 );
+
+ pNpScb->BurstDataWritten = Length;
+
+ IrpContext->Flags = Flags;
+ SetFlag( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ }
+
+ IrpContext->TimeoutRoutine = BurstWriteTimeout;
+ IrpContext->CompletionSendRoutine = BurstWriteCompletionSend;
+ IrpContext->pTdiStruct = &IrpContext->pNpScb->Burst;
+ IrpContext->PacketType = NCP_BURST;
+ IrpContext->pEx = BurstWriteCallback;
+
+ IrpContext->Specific.Write.FullMdl = DataMdl;
+
+ MmGetSystemAddressForMdl( DataMdl );
+
+ //
+ // Allocate a partial Mdl for the worst possible case of alignment
+ //
+
+ IrpContext->Specific.Write.PartialMdl =
+ ALLOCATE_MDL( 0, IrpContext->pNpScb->MaxPacketSize + PAGE_SIZE - 1, FALSE, FALSE, NULL);
+
+ if ( IrpContext->Specific.Write.PartialMdl == NULL ) {
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Get to the front of the SCB queue, if we are not already there.
+ // Note that can't append this IrpContext to the SCB until after
+ // the probe and lock, since the probe and lock may cause a paging
+ // read on this SCB.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ status = SendWriteBurst(
+ IrpContext,
+ BURST_WRITE_HEADER_SIZE,
+ (USHORT)Length,
+ TRUE,
+ FALSE );
+
+ MissingData = TRUE;
+ while ( MissingData ) {
+
+ KeWaitForSingleObject( &IrpContext->Event, Executive, KernelMode, FALSE, NULL );
+ MmPrepareMdlForReuse( IrpContext->Specific.Write.PartialMdl );
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // This burst has timed out, simply resend the burst.
+ //
+
+ NwProcessSendBurstFailure( pNpScb, 1 );
+
+ status = SendWriteBurst(
+ IrpContext,
+ BURST_WRITE_HEADER_SIZE,
+ (USHORT)Length,
+ TRUE,
+ TRUE );
+ continue;
+ }
+
+ if ( !NT_SUCCESS( IrpContext->Specific.Write.Status ) ) {
+
+ status = IrpContext->Specific.Write.Status;
+ Done = TRUE;
+
+ goto EndOfLoop;
+
+ } else {
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "B_d",
+ &BurstFlags,
+ 8,
+ &Result );
+
+ }
+
+ if ( BurstFlags & BURST_FLAG_SYSTEM_PACKET ) {
+
+ //
+ // The server dropped at least one packet.
+ //
+
+ MissingData = TRUE;
+ DebugTrace( 0, Dbg, "Received system packet\n", 0 );
+
+ //
+ // This is a missing fragment request.
+ //
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "G_w",
+ 34,
+ &MissingFragmentCount );
+
+ ASSERT( NT_SUCCESS( status ) );
+ ASSERT( MissingFragmentCount != 0 );
+
+ NwProcessSendBurstFailure( pNpScb, MissingFragmentCount );
+
+ DebugTrace( 0, Dbg, "Received request for %d missing fragment\n", MissingFragmentCount );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Walk the missing fragment list and send the missing fragments.
+ //
+
+ for ( i = 0; i < MissingFragmentCount && NT_SUCCESS( status ); i++ ) {
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "G_dw",
+ 34 + 2 + 6 * i,
+ &FragmentOffset,
+ &FragmentLength
+ );
+
+ ASSERT( NT_SUCCESS( status ) );
+
+ if ( FragmentOffset < Length + BURST_WRITE_HEADER_SIZE &&
+ FragmentOffset + FragmentLength <=
+ Length + BURST_WRITE_HEADER_SIZE ) {
+
+ //
+ // Send a burst with the missing data. Do no set the
+ // end of burst bit until we have sent the last
+ // missing fragment packet.
+ //
+
+ status = SendWriteBurst(
+ IrpContext,
+ FragmentOffset,
+ FragmentLength,
+ (BOOLEAN)( i == (MissingFragmentCount - 1)),
+ FALSE );
+ } else {
+
+ //
+ // Received a bogus missing fragment request.
+ // Ignore the remainder of the request.
+ //
+
+ status = STATUS_INVALID_NETWORK_RESPONSE;
+ Done = TRUE;
+
+ goto EndOfLoop;
+
+ }
+ }
+
+ Stats.PacketBurstWriteTimeouts++;
+
+ } else {
+
+ NwProcessSendBurstSuccess( pNpScb );
+
+ MissingData = FALSE;
+
+ //
+ // This is not a system packets, check the response.
+ //
+
+ if ( Result == 0 ) {
+
+ //
+ // If the last write worked then move the pointers appropriately
+ //
+
+ LastLength = IrpContext->Specific.Write.LastWriteLength;
+
+ IrpContext->Specific.Write.RemainingLength -= LastLength;
+ IrpContext->Specific.Write.WriteOffset += LastLength;
+ IrpContext->Specific.Write.FileOffset += LastLength;
+
+ //
+ // If this is a print job, remember that we actually wrote data
+ //
+
+ if ( IrpContext->Icb->IsPrintJob ) {
+ IrpContext->Icb->ActuallyPrinted = TRUE;
+ }
+
+ } else {
+
+ //
+ // Abandon this request
+ //
+
+ Done = TRUE;
+ }
+
+
+ //
+ // Do we need to send another burst to satisfy the write IRP?
+ //
+
+ if ( IrpContext->Specific.Write.RemainingLength != 0 ) {
+
+ //
+ // Write the next packet.
+ //
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n", IrpContext->Specific.Write.RemainingLength);
+ DebugTrace( 0, Dbg, "FileOffset = %ld\n", IrpContext->Specific.Write.FileOffset);
+ DebugTrace( 0, Dbg, "WriteOffset = %ld\n", IrpContext->Specific.Write.WriteOffset);
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->MaxSendSize,
+ IrpContext->Specific.Write.RemainingLength );
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ } else {
+ Done = TRUE;
+ }
+
+ } // else ( not a system packet )
+
+ } // while ( missing data )
+
+ //
+ // Update the burst request number now.
+ //
+
+ if ( status != STATUS_REMOTE_NOT_LISTENING ) {
+ IrpContext->pNpScb->BurstRequestNo++;
+ }
+
+ //
+ // If we need to reconnect, do it now.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ BurstWriteReconnect( IrpContext );
+ }
+
+ //
+ // Dequeue this Irp context in preparation for the next run
+ // through the loop.
+ //
+
+EndOfLoop:
+ ASSERT( status != STATUS_PENDING );
+
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+
+ //
+ // Unlock locked pages, and free our MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ } while ( !Done );
+
+ DebugTrace(-1, Dbg, "BurstWrite -> %08lx\n", status );
+ return status;
+}
+
+#ifdef NWDBG
+int DropWritePackets;
+#endif
+
+
+NTSTATUS
+BurstWriteCompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine handles completion of a burst write send. If the sending
+ thread is waiting for send completion notification, it signals the
+ IrpContext Event.
+
+ Note that this routine can be called from SendWriteBurst (i.e. not
+ at DPC level), if an allocation fails.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PIRP_CONTEXT pIrpContext = (PIRP_CONTEXT) Context;
+ INTERLOCKED_RESULT Result;
+ KIRQL OldIrql;
+ NTSTATUS Status;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "BurstWriteCompletionSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", pIrpContext);
+
+ if ( Irp != NULL ) {
+
+ DebugTrace( 0, Dbg, "Burst Write Send = %08lx\n", Irp->IoStatus.Status );
+
+ Status = Irp->IoStatus.Status;
+
+ } else {
+
+ Status = STATUS_SUCCESS;
+
+ }
+
+ //
+ // If this was a secondary IRP, free it now.
+ //
+
+ if ( pIrpContext->NodeTypeCode == NW_NTC_MINI_IRP_CONTEXT ) {
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ MiniIrpContext = (PMINI_IRP_CONTEXT)pIrpContext;
+
+ ASSERT( MiniIrpContext->Mdl2->Next == NULL );
+
+ pIrpContext = MiniIrpContext->IrpContext;
+ FreeMiniIrpContext( MiniIrpContext );
+
+ }
+
+ //
+ // Nothing to do unless the last send has completed.
+ //
+
+ Result = ExInterlockedDecrementLong(
+ &pIrpContext->Specific.Write.PacketCount,
+ &pIrpContext->pNpScb->NpScbInterLock );
+
+ if ( Result != RESULT_ZERO ) {
+ DebugTrace( 0, Dbg, "Packets to go = %d\n", pIrpContext->Specific.Write.PacketCount );
+
+ if (Status == STATUS_BAD_NETWORK_PATH) {
+
+ //
+ // IPX has ripped for the destination but failed to find the net. Minimise the
+ // difference between this case and sending a normal burst by completing the
+ // transmission as soon as possible.
+ //
+
+ pIrpContext->pNpScb->NwSendDelay = 0;
+
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ KeAcquireSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ ASSERT( pIrpContext->pNpScb->Sending == TRUE );
+ pIrpContext->pNpScb->Sending = FALSE;
+
+ //
+ // Signal to the writing thread that the send has completed, if it
+ // is waiting.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_SIGNAL_EVENT ) ) {
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_SIGNAL_EVENT );
+ NwSetIrpContextEvent( pIrpContext );
+ }
+
+ //
+ // If we processed a receive while waiting for send
+ // completion call the receive handler routine now.
+ //
+
+ if ( pIrpContext->pNpScb->Received ) {
+
+ pIrpContext->pNpScb->Receiving = FALSE;
+ pIrpContext->pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ pIrpContext->pEx(
+ pIrpContext,
+ pIrpContext->ResponseLength,
+ pIrpContext->rsp );
+
+ } else {
+ if ((Status == STATUS_BAD_NETWORK_PATH) &&
+ (pIrpContext->pNpScb->Receiving == FALSE)) {
+
+ //
+ // Usually means a ras connection has gone down during the burst.
+ // Go through the timeout logic now because the ras timeouts take
+ // a long time and unless we re rip things won't get better.
+ //
+
+ pIrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ NwSetIrpContextEvent( pIrpContext );
+
+ }
+
+ KeReleaseSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ DebugTrace( -1, Dbg, "BurstWriteCompletionSend -> STATUS_MORE_PROCESSING_REQUIRED\n", 0);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+}
+
+
+NTSTATUS
+BurstWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response a burst write.
+
+Arguments:
+
+ IrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ Response - Points to the receive buffer.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DebugTrace(0, Dbg, "BurstWriteCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->Write.Status
+ // Clear the retry send bit so we don't keep retrying.
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace(-1, Dbg, "BurstWriteCallback -> %X\n", STATUS_REMOTE_NOT_LISTENING );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ IrpContext->Specific.Write.Status = STATUS_SUCCESS;
+ ASSERT( BytesAvailable < MAX_RECV_DATA );
+ ++Stats.PacketBurstWriteNcps;
+
+ //
+ // Clear the retry send bit, since we have a response.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Copy the burst write response, and signal the users thread
+ // to continue.
+ //
+
+ TdiCopyLookaheadData(
+ IrpContext->rsp,
+ Response,
+ BytesAvailable < MAX_RECV_DATA ? BytesAvailable : MAX_RECV_DATA,
+ 0
+ );
+
+ IrpContext->ResponseLength = BytesAvailable;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SendWriteBurst(
+ PIRP_CONTEXT IrpContext,
+ ULONG BurstOffset,
+ USHORT Length,
+ BOOLEAN EndOfBurst,
+ BOOLEAN Retransmission
+ )
+/*++
+
+Routine Description:
+
+ This routine does the actual work of sending a series of burst write
+ NCPs to the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BurstOffset - The offset in the burst to start sending. If BurstOffset
+ equals BURST_WRITE_HEADER_SIZE, start from the beginning of the burst.
+
+ Length - The length of the burst.
+
+ EndOfBurst - If TRUE set the end of burst bit when sending the last
+ frame. Otherwise there is more (discontiguous) data to come in
+ the current burst.
+
+ Retransmission - If TRUE, this is a burst write timeout retransmission.
+ Send the first packet only.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ UCHAR BurstFlags;
+ NTSTATUS Status;
+ BOOLEAN MoreData;
+ PIRP SendIrp;
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "SendWriteBurst...\n", 0);
+
+ DebugTrace( 0, Dbg, "Data offset = %d\n", BurstOffset );
+ DebugTrace( 0, Dbg, "Data length = %d\n", Length );
+ DebugTrace( 0, Dbg, "End of burst = %d\n", EndOfBurst );
+
+ //
+ // Send the request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+
+ //
+ // Set the burst flags
+ //
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( IrpContext->pNpScb->MaxPacketSize, Length );
+
+ //
+ // Set the end-of-burst bit (and enable receiving the response), if this
+ // is the last packet we expect to send.
+ //
+
+ if ( ( !EndOfBurst || IrpContext->Specific.Write.BurstLength < Length )
+ && !Retransmission ) {
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = 0;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Last packet in the burst\n", 0);
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = BURST_FLAG_END_OF_BURST;
+
+ }
+
+ if ( !EndOfBurst ) {
+ SetFlag( IrpContext->Flags, IRP_FLAG_SIGNAL_EVENT );
+ }
+
+ //
+ // Build the partial MDL for the first packet in the burst.
+ //
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ BurstOffset - BURST_WRITE_HEADER_SIZE,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // Set the burst flags
+ //
+
+ if ( BurstOffset == BURST_WRITE_HEADER_SIZE ) {
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+ }
+
+ if ( ( IrpContext->Specific.Write.BurstLength < Length ) &&
+ !Retransmission ) {
+ MoreData = TRUE;
+ } else {
+ MoreData = FALSE;
+ }
+
+ if ( BurstOffset == BURST_WRITE_HEADER_SIZE ) {
+
+ BuildBurstWriteFirstReq(
+ IrpContext,
+ IrpContext->req,
+ Length,
+ IrpContext->Specific.Write.PartialMdl,
+ BurstFlags,
+ *(ULONG UNALIGNED *)(&IrpContext->Icb->Handle[2]),
+ IrpContext->Specific.Write.FileOffset );
+
+ } else {
+
+ BuildBurstWriteNextReq(
+ IrpContext,
+ IrpContext->req,
+ IrpContext->Specific.Write.LastWriteLength + BURST_WRITE_HEADER_SIZE,
+ BurstFlags,
+ BurstOffset,
+ IrpContext->TxMdl,
+ IrpContext->Specific.Write.PartialMdl
+ );
+
+ }
+
+ if ( !Retransmission ) {
+ IrpContext->Specific.Write.PacketCount =
+ ( Length + IrpContext->pNpScb->MaxPacketSize - 1 ) /
+ IrpContext->pNpScb->MaxPacketSize;
+
+ } else {
+ IrpContext->Specific.Write.PacketCount = 1;
+ }
+
+ DebugTrace( 0, Dbg, "Packet count = %d\n", IrpContext->Specific.Write.PacketCount );
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Send delay = %d\n", IrpContext->pNpScb->NwSendDelay );
+
+ //
+ // Use the original IRP context to format the first packet.
+ //
+
+ ++Stats.PacketBurstWriteNcps;
+ PreparePacket( IrpContext, IrpContext->pOriginalIrp, IrpContext->TxMdl );
+
+ Status = SendPacket( IrpContext, IrpContext->pNpScb );
+
+ while ( MoreData ) {
+
+ if ( IrpContext->pNpScb->NwSendDelay > 0 ) {
+
+ //
+ // Introduce a send delay between packets.
+ //
+
+ KeDelayExecutionThread(
+ KernelMode,
+ FALSE,
+ &IrpContext->pNpScb->NtSendDelay );
+ }
+
+ MiniIrpContext = AllocateMiniIrpContext( IrpContext );
+
+ DebugTrace( 0, Dbg, "Allocated mini IrpContext = %X\n", MiniIrpContext );
+
+ //
+ // Calculate the total number of bytes to send during this burst. Do this before
+ // checking to see if MiniIrpContext is NULL so that we skip the packet rather
+ // than sitting in a tight loop.
+ //
+
+ BurstOffset += IrpContext->Specific.Write.BurstLength;
+
+ //
+ // Do we need to send another burst write packet?
+ //
+
+ Length -= (USHORT)IrpContext->Specific.Write.BurstLength;
+
+ ASSERT ( Length > 0 );
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( IrpContext->pNpScb->MaxPacketSize, (ULONG)Length );
+
+ DebugTrace( +0, Dbg, "More data, sending %d bytes\n", IrpContext->Specific.Write.BurstLength );
+
+ //
+ // If we can't allocate a mini irp context to send the packet,
+ // just skip it and wait for the server to ask a retranmit. At
+ // this point performance isn't exactly stellar, so don't worry
+ // about having to wait for a timeout.
+ //
+
+ if ( MiniIrpContext == NULL ) {
+
+ ExInterlockedDecrementLong(
+ &IrpContext->Specific.Write.PacketCount,
+ &IrpContext->pNpScb->NpScbInterLock );
+
+ continue;
+ }
+
+#ifdef NWDBG
+
+ //
+ // If DropWritePackets is enabled, simulate missing packets, by
+ // occasionally dropping 500 bytes of data.
+ //
+
+ if ( DropWritePackets != 0 ) {
+ if ( ( rand() % DropWritePackets ) == 0 &&
+ Length != IrpContext->Specific.Write.BurstLength ) {
+
+ FreeMiniIrpContext( MiniIrpContext );
+
+ ExInterlockedDecrementLong(
+ &IrpContext->Specific.Write.PacketCount,
+ &IrpContext->pNpScb->NpScbInterLock );
+
+ continue;
+ }
+ }
+#endif
+
+ //
+ // Build the MDL for the data to send.
+ //
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ MiniIrpContext->Mdl2,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ BurstOffset - BURST_WRITE_HEADER_SIZE,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // Set the burst flags
+ //
+
+ if ( !EndOfBurst || IrpContext->Specific.Write.BurstLength < Length ) {
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = 0;
+ } else {
+ DebugTrace( 0, Dbg, "Last packet in the burst\n", 0);
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = BURST_FLAG_END_OF_BURST;
+ }
+
+ if ( IrpContext->Specific.Write.BurstLength == Length ) {
+ MoreData = FALSE;
+ }
+
+ BuildBurstWriteNextReq(
+ IrpContext,
+ MiniIrpContext->Mdl1->MappedSystemVa,
+ IrpContext->Specific.Write.LastWriteLength +
+ BURST_WRITE_HEADER_SIZE,
+ BurstFlags,
+ BurstOffset,
+ MiniIrpContext->Mdl1,
+ MiniIrpContext->Mdl2
+ );
+
+ ++Stats.PacketBurstWriteNcps;
+
+ SendIrp = MiniIrpContext->Irp;
+
+ PreparePacket( IrpContext, SendIrp, MiniIrpContext->Mdl1 );
+
+ // BUGBUG Clean this up
+ IoSetCompletionRoutine( SendIrp, BurstWriteCompletionSend, MiniIrpContext, TRUE, TRUE, TRUE);
+
+ ASSERT( MiniIrpContext->Mdl2->Next == NULL );
+
+ Status = SendSecondaryPacket( IrpContext, SendIrp );
+ }
+
+ //
+ // If this is not the end-of-burst, wait for send completion here,
+ // since the caller is about to send more data.
+ //
+
+ if ( !EndOfBurst ) {
+ KeWaitForSingleObject( &IrpContext->Event, Executive, KernelMode, FALSE, NULL );
+ }
+
+ DebugTrace( -1, Dbg, "SendWriteBurst -> %X\n", Status );
+ return( Status );
+}
+
+
+VOID
+BurstWriteTimeout(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a burst write timeout.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PIRP Irp;
+
+ DebugTrace(0, Dbg, "BurstWriteTimeout\n", 0 );
+
+ Irp = IrpContext->pOriginalIrp;
+
+ //
+ // Set the RetrySend flag, so that we know to retransmit the request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Signal the write thread to wakeup and resend the burst.
+ //
+
+ NwSetIrpContextEvent( IrpContext );
+
+ Stats.PacketBurstWriteTimeouts++;
+
+ return;
+}
+
+
+NTSTATUS
+ResubmitBurstWrite(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine resubmits a burst write over a new burst connection.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+
+ PAGED_CODE();
+
+ //
+ // Remember that we need to establish a new burst connection.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ //
+ // Set the packet size down the largest packet we can use, that
+ // is guaranteed to be routable.
+ //
+
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+
+ //
+ // Crank the delay times down so we give the new connection a chance.
+ //
+
+ pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay = MinSendDelay;
+ pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay = MinReceiveDelay;
+
+ pNpScb->SendBurstSuccessCount = 0;
+ pNpScb->ReceiveBurstSuccessCount = 0;
+
+ pNpScb->NtSendDelay.QuadPart = MinSendDelay;
+
+ //
+ // Signal the write thread to wakeup and resend the burst.
+ //
+
+ NwSetIrpContextEvent( IrpContext );
+
+ return( STATUS_PENDING );
+}
+
+
+NTSTATUS
+BurstWriteReconnect(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a new IRP context and renegotiates burst mode.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PIRP_CONTEXT pNewIrpContext;
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+ BOOLEAN LIPNegotiated ;
+
+ PAGED_CODE();
+
+ //
+ // Attempt to allocate an extra IRP context.
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pNewIrpContext->Specific.Create.UserUid = IrpContext->Specific.Create.UserUid;
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+ pNewIrpContext->pNpScb = pNpScb;
+
+ //
+ // Insert this new IrpContext to the head of
+ // the SCB queue for processing. We can get away with this
+ // because we own the IRP context currently at the front of
+ // the queue.
+ //
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ //
+ // Renegotiate the burst connection, this will automatically re-sync
+ // the burst connection.
+ //
+
+ NegotiateBurstMode( pNewIrpContext, pNpScb, &LIPNegotiated );
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ ExInterlockedRemoveHeadList(
+ &pNpScb->Requests,
+ &pNpScb->NpScbSpinLock );
+
+ ClearFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwFsdFlushBuffers(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtFlushBuffersFile.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the write function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdFlushBuffers\n", 0);
+
+ //
+ // Call the common write routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonFlushBuffers( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdFlushBuffers -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonFlushBuffers (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine requests all dirty cache buffers to be flushed for a
+ given file.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ NTSTATUS Status;
+ PFCB Fcb;
+ PICB Icb;
+ NODE_TYPE_CODE NodeTypeCode;
+ PVOID FsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "NwCommonFlushBuffers...\n", 0);
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the a file then its an illegal parameter.
+ //
+
+ if (( NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "NwCommonFlushBuffers -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( Icb );
+
+ Fcb = (PFCB)Icb->SuperType.Fcb;
+ NodeTypeCode = Fcb->NodeTypeCode;
+
+ if ( NodeTypeCode != NW_NTC_FCB ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonFlushBuffers -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Set up the IRP context to do an exchange
+ //
+
+ IrpContext->pScb = Fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = Icb;
+
+ //
+ // Send any user data to the server. Note we must not be on the
+ // queue when we do this.
+ //
+
+ MmFlushImageSection(&Icb->NpFcb->SegmentObject, MmFlushForWrite);
+
+ //
+ // Flush our dirty data.
+ //
+
+ Status = AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Send a flush NCP
+ //
+
+ Status = Exchange (
+ IrpContext,
+ FlushBuffersCallback,
+ "F-r",
+ NCP_FLUSH_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ) );
+
+ return( Status );
+}
+
+
+NTSTATUS
+FlushBuffersCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the flush file size response and completes the
+ flush IRP.
+
+Arguments:
+
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DebugTrace(0, Dbg, "FlushBuffersCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N" );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return Status;
+}
+
+
+VOID
+BuildBurstWriteFirstReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ PMDL BurstMdl,
+ UCHAR Flags,
+ ULONG Handle,
+ ULONG FileOffset
+ )
+{
+ PNCP_BURST_WRITE_REQUEST BurstWrite;
+ PNONPAGED_SCB pNpScb;
+ ULONG RealDataLength;
+ USHORT RealBurstLength;
+
+ PAGED_CODE();
+
+ BurstWrite = (PNCP_BURST_WRITE_REQUEST)Buffer;
+ pNpScb = IrpContext->pNpScb;
+
+ RealDataLength = DataSize + sizeof( *BurstWrite ) - sizeof( NCP_BURST_HEADER );
+ RealBurstLength = (USHORT)MdlLength( BurstMdl ) + sizeof( *BurstWrite ) - sizeof( NCP_BURST_HEADER );
+
+ BurstWrite->BurstHeader.Command = PEP_COMMAND_BURST;
+ BurstWrite->BurstHeader.Flags = Flags;
+ BurstWrite->BurstHeader.StreamType = 0x02;
+ BurstWrite->BurstHeader.SourceConnection = pNpScb->SourceConnectionId;
+ BurstWrite->BurstHeader.DestinationConnection = pNpScb->DestinationConnectionId;
+
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // Use the same delay on all retransmissions of the burst. Save
+ // the current time.
+ //
+
+ pNpScb->CurrentBurstDelay = pNpScb->NwSendDelay;
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ } else {
+
+ //
+ // This is a retransmission. Alternate between sending a system
+ // packet and the first write.
+ //
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET ) ) {
+
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ BurstWrite->BurstHeader.Flags = BURST_FLAG_SYSTEM_PACKET;
+
+ LongByteSwap( BurstWrite->BurstHeader.SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ BurstWrite->BurstHeader.DataSize = 0;
+ BurstWrite->BurstHeader.BurstOffset = 0;
+ BurstWrite->BurstHeader.BurstLength = 0;
+ BurstWrite->BurstHeader.MissingFragmentCount = 0;
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_HEADER );
+ IrpContext->TxMdl->Next = NULL;
+
+ return;
+
+ }
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ }
+
+ LongByteSwap( BurstWrite->BurstHeader.SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ LongByteSwap( BurstWrite->BurstHeader.DataSize, RealDataLength );
+ BurstWrite->BurstHeader.BurstOffset = 0;
+ ShortByteSwap( BurstWrite->BurstHeader.BurstLength, RealBurstLength );
+ BurstWrite->BurstHeader.MissingFragmentCount = 0;
+
+ BurstWrite->Function = BURST_REQUEST_WRITE;
+ BurstWrite->Handle = Handle;
+ LongByteSwap( BurstWrite->TotalWriteOffset, IrpContext->Specific.Write.TotalWriteOffset );
+ LongByteSwap( BurstWrite->TotalWriteLength, IrpContext->Specific.Write.TotalWriteLength );
+ LongByteSwap( BurstWrite->Offset, FileOffset );
+ LongByteSwap( BurstWrite->Length, DataSize );
+
+ IrpContext->TxMdl->ByteCount = sizeof( *BurstWrite );
+ IrpContext->TxMdl->Next = BurstMdl;
+
+ return;
+}
+
+VOID
+BuildBurstWriteNextReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ UCHAR BurstFlags,
+ ULONG BurstOffset,
+ PMDL BurstHeaderMdl,
+ PMDL BurstDataMdl
+ )
+{
+ PNCP_BURST_HEADER BurstHeader;
+ PNONPAGED_SCB pNpScb;
+ USHORT BurstLength;
+
+ PAGED_CODE();
+
+ BurstHeader = (PNCP_BURST_HEADER)Buffer;
+ pNpScb = IrpContext->pNpScb;
+
+ BurstLength = (USHORT)MdlLength( BurstDataMdl );
+
+ BurstHeader->Command = PEP_COMMAND_BURST;
+ BurstHeader->Flags = BurstFlags;
+ BurstHeader->StreamType = 0x02;
+ BurstHeader->SourceConnection = pNpScb->SourceConnectionId;
+ BurstHeader->DestinationConnection = pNpScb->DestinationConnectionId;
+
+ LongByteSwap( BurstHeader->SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // This is a retransmission. Alternate between sending a system
+ // packet and the first write.
+ //
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET ) ) {
+
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ BurstHeader->Flags = BURST_FLAG_SYSTEM_PACKET;
+
+ LongByteSwap( BurstHeader->SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ BurstHeader->DataSize = 0;
+ BurstHeader->BurstOffset = 0;
+ BurstHeader->BurstLength = 0;
+ BurstHeader->MissingFragmentCount = 0;
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_HEADER );
+ IrpContext->TxMdl->Next = NULL;
+
+ return;
+
+ }
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ } else {
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ }
+
+ LongByteSwap( BurstHeader->DataSize, DataSize );
+ LongByteSwap( BurstHeader->BurstOffset, BurstOffset );
+ ShortByteSwap( BurstHeader->BurstLength, BurstLength );
+ BurstHeader->MissingFragmentCount = 0;
+
+ BurstHeaderMdl->ByteCount = sizeof( *BurstHeader );
+ BurstHeaderMdl->Next = BurstDataMdl;
+
+ return;
+}
+
+
+NTSTATUS
+SendSecondaryPacket(
+ PIRP_CONTEXT IrpContext,
+ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine submits a TDI send request to the tranport layer.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+ Irp - The IRP for the packet to send.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+ PNCP_BURST_HEADER BurstHeader;
+ pNpScb = IrpContext->pNpScb;
+
+ DebugTrace( 0, Dbg, "SendSecondaryPacket\n", 0 );
+
+ BurstHeader = (PNCP_BURST_HEADER)( MmGetMdlVirtualAddress( Irp->MdlAddress ) );
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE ) ) {
+ pNpScb->OkToReceive = TRUE;
+ }
+
+ LongByteSwap( BurstHeader->PacketSequenceNo, pNpScb->BurstSequenceNo );
+ pNpScb->BurstSequenceNo++;
+
+ ShortByteSwap( BurstHeader->BurstSequenceNo, pNpScb->BurstRequestNo );
+ ShortByteSwap( BurstHeader->AckSequenceNo, pNpScb->BurstRequestNo );
+
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp );
+ DebugTrace( +0, Dbg, "pIrpC %X\n", IrpContext);
+
+#if NWDBG
+ dumpMdl( Dbg, IrpContext->TxMdl);
+#endif
+
+ Stats.BytesTransmitted.QuadPart += MdlLength( Irp->MdlAddress );
+ Stats.NcpsTransmitted.QuadPart += 1;
+
+ Status = IoCallDriver( pNpScb->Server.pDeviceObject, Irp );
+ DebugTrace( -1, Dbg, " %X\n", Status );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Error( EVENT_NWRDR_NETWORK_ERROR, Status, NULL, 0, 0 );
+ }
+
+ return Status;
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastWrite (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine does a fast cached read bypassing the usual file system
+ entry routine (i.e., without the Irp). It is used to do a copy read
+ of a cached file object. For a complete description of the arguments
+ see CcCopyRead.
+
+Arguments:
+
+ FileObject - Pointer to the file object being read.
+
+ FileOffset - Byte offset in file for desired data.
+
+ Length - Length of desired data in bytes.
+
+ Wait - FALSE if caller may not block, TRUE otherwise
+
+ Buffer - Pointer to output buffer to which data should be copied.
+
+ IoStatus - Pointer to standard I/O status block to receive the status
+ for the transfer.
+
+Return Value:
+
+ FALSE - if Wait was supplied as FALSE and the data was not delivered, or
+ if there is an I/O error.
+
+ TRUE - if the data is being delivered
+
+--*/
+
+{
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+ ULONG offset;
+ BOOLEAN wroteToCache;
+
+ DebugTrace(+1, Dbg, "NwFastWrite...\n", 0);
+
+ //
+ // Special case a read of zero length
+ //
+
+ if (Length == 0) {
+
+ //
+ // A zero length transfer was requested.
+ //
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = 0;
+
+ DebugTrace(+1, Dbg, "NwFastWrite -> TRUE\n", 0);
+ return TRUE;
+ }
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not FCB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ DebugTrace(-1, Dbg, "NwFastWrite -> FALSE\n", 0);
+ return FALSE;
+ }
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+ offset = FileOffset->LowPart;
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = Length;
+
+ wroteToCache = CacheWrite(
+ NULL,
+ fcb->NonPagedFcb,
+ offset,
+ Length,
+ Buffer );
+
+ DebugTrace(-1, Dbg, "NwFastWrite -> %s\n", wroteToCache ? "TRUE" : "FALSE" );
+
+ if ( wroteToCache ) {
+
+ //
+ // If the file was extended, record the new file size.
+ //
+
+ if ( ( offset + Length ) > fcb->NonPagedFcb->Header.FileSize.LowPart ) {
+ fcb->NonPagedFcb->Header.FileSize.LowPart = ( offset + Length );
+ }
+ }
+
+#ifndef NT1057
+
+ //
+ // Update the file object if we succeeded. We know that this
+ // is synchronous and not paging io because it's coming in through
+ // the cache.
+ //
+
+ if ( wroteToCache ) {
+ FileObject->CurrentByteOffset.QuadPart += Length;
+ }
+
+#endif
+
+ return( wroteToCache );
+
+}
+#endif