From e611b132f9b8abe35b362e5870b74bce94a1e58e Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 16 May 2020 20:51:50 -0700 Subject: initial commit --- private/nw/rdr/attach.c | 5169 ++++++++++++++++++++++++++++ private/nw/rdr/cache.c | 698 ++++ private/nw/rdr/callback.c | 190 ++ private/nw/rdr/cleanup.c | 591 ++++ private/nw/rdr/close.c | 571 ++++ private/nw/rdr/const.h | 166 + private/nw/rdr/convert.c | 732 ++++ private/nw/rdr/convert.h | 33 + private/nw/rdr/create.c | 3398 +++++++++++++++++++ private/nw/rdr/create4.c | 2075 ++++++++++++ private/nw/rdr/crypto.h | 139 + private/nw/rdr/data.c | 373 +++ private/nw/rdr/data.h | 238 ++ private/nw/rdr/debug.c | 780 +++++ private/nw/rdr/deviosup.c | 153 + private/nw/rdr/dir.c | 1672 ++++++++++ private/nw/rdr/encrypt.c | 322 ++ private/nw/rdr/errorlog.c | 201 ++ private/nw/rdr/except.c | 160 + private/nw/rdr/exchange.c | 4887 +++++++++++++++++++++++++++ private/nw/rdr/exchange.h | 114 + private/nw/rdr/fileinfo.c | 2905 ++++++++++++++++ private/nw/rdr/filobsup.c | 175 + private/nw/rdr/fragex.c | 783 +++++ private/nw/rdr/fsctl.c | 5930 +++++++++++++++++++++++++++++++++ private/nw/rdr/fspdisp.c | 232 ++ private/nw/rdr/init.c | 460 +++ private/nw/rdr/ipx.c | 1749 ++++++++++ private/nw/rdr/kdext/dirs | 26 + private/nw/rdr/kdext/ntsd/makefile | 6 + private/nw/rdr/kdext/ntsd/nw.def | 15 + private/nw/rdr/kdext/ntsd/nw.rc | 12 + private/nw/rdr/kdext/ntsd/sources | 49 + private/nw/rdr/kdext/nwrdrkd.c | 2223 ++++++++++++ private/nw/rdr/kdext/windbg/makefile | 6 + private/nw/rdr/kdext/windbg/nwdbg.def | 15 + private/nw/rdr/kdext/windbg/nwdbg.rc | 12 + private/nw/rdr/kdext/windbg/sources | 51 + private/nw/rdr/lock.c | 1357 ++++++++ private/nw/rdr/lockcode.c | 168 + private/nw/rdr/makefile | 6 + private/nw/rdr/ndsfsctl.c | 2128 ++++++++++++ private/nw/rdr/ndslogin.c | 3365 +++++++++++++++++++ private/nw/rdr/ndsprocs.h | 822 +++++ private/nw/rdr/ndsread.c | 1190 +++++++ private/nw/rdr/nodetype.h | 63 + private/nw/rdr/nwrdr.rc | 11 + private/nw/rdr/pid.c | 454 +++ private/nw/rdr/procs.h | 1830 ++++++++++ private/nw/rdr/read.c | 2838 ++++++++++++++++ private/nw/rdr/scavengr.c | 689 ++++ private/nw/rdr/security.c | 1009 ++++++ private/nw/rdr/sources | 90 + private/nw/rdr/string.c | 378 +++ private/nw/rdr/strucsup.c | 3127 +++++++++++++++++ private/nw/rdr/struct.h | 1357 ++++++++ private/nw/rdr/synch.txt | 74 + private/nw/rdr/timer.c | 537 +++ private/nw/rdr/util.c | 385 +++ private/nw/rdr/volinfo.c | 1278 +++++++ private/nw/rdr/workque.c | 829 +++++ private/nw/rdr/write.c | 3063 +++++++++++++++++ 62 files changed, 64359 insertions(+) create mode 100644 private/nw/rdr/attach.c create mode 100644 private/nw/rdr/cache.c create mode 100644 private/nw/rdr/callback.c create mode 100644 private/nw/rdr/cleanup.c create mode 100644 private/nw/rdr/close.c create mode 100644 private/nw/rdr/const.h create mode 100644 private/nw/rdr/convert.c create mode 100644 private/nw/rdr/convert.h create mode 100644 private/nw/rdr/create.c create mode 100644 private/nw/rdr/create4.c create mode 100644 private/nw/rdr/crypto.h create mode 100644 private/nw/rdr/data.c create mode 100644 private/nw/rdr/data.h create mode 100644 private/nw/rdr/debug.c create mode 100644 private/nw/rdr/deviosup.c create mode 100644 private/nw/rdr/dir.c create mode 100644 private/nw/rdr/encrypt.c create mode 100644 private/nw/rdr/errorlog.c create mode 100644 private/nw/rdr/except.c create mode 100644 private/nw/rdr/exchange.c create mode 100644 private/nw/rdr/exchange.h create mode 100644 private/nw/rdr/fileinfo.c create mode 100644 private/nw/rdr/filobsup.c create mode 100644 private/nw/rdr/fragex.c create mode 100644 private/nw/rdr/fsctl.c create mode 100644 private/nw/rdr/fspdisp.c create mode 100644 private/nw/rdr/init.c create mode 100644 private/nw/rdr/ipx.c create mode 100644 private/nw/rdr/kdext/dirs create mode 100644 private/nw/rdr/kdext/ntsd/makefile create mode 100644 private/nw/rdr/kdext/ntsd/nw.def create mode 100644 private/nw/rdr/kdext/ntsd/nw.rc create mode 100644 private/nw/rdr/kdext/ntsd/sources create mode 100644 private/nw/rdr/kdext/nwrdrkd.c create mode 100644 private/nw/rdr/kdext/windbg/makefile create mode 100644 private/nw/rdr/kdext/windbg/nwdbg.def create mode 100644 private/nw/rdr/kdext/windbg/nwdbg.rc create mode 100644 private/nw/rdr/kdext/windbg/sources create mode 100644 private/nw/rdr/lock.c create mode 100644 private/nw/rdr/lockcode.c create mode 100644 private/nw/rdr/makefile create mode 100644 private/nw/rdr/ndsfsctl.c create mode 100644 private/nw/rdr/ndslogin.c create mode 100644 private/nw/rdr/ndsprocs.h create mode 100644 private/nw/rdr/ndsread.c create mode 100644 private/nw/rdr/nodetype.h create mode 100644 private/nw/rdr/nwrdr.rc create mode 100644 private/nw/rdr/pid.c create mode 100644 private/nw/rdr/procs.h create mode 100644 private/nw/rdr/read.c create mode 100644 private/nw/rdr/scavengr.c create mode 100644 private/nw/rdr/security.c create mode 100644 private/nw/rdr/sources create mode 100644 private/nw/rdr/string.c create mode 100644 private/nw/rdr/strucsup.c create mode 100644 private/nw/rdr/struct.h create mode 100644 private/nw/rdr/synch.txt create mode 100644 private/nw/rdr/timer.c create mode 100644 private/nw/rdr/util.c create mode 100644 private/nw/rdr/volinfo.c create mode 100644 private/nw/rdr/workque.c create mode 100644 private/nw/rdr/write.c (limited to 'private/nw/rdr') 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 // 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 \ + // + + if ( PathName->Buffer == NULL ) { + + // return just or NULL + + *FullName = *FileName; + + } else { + // Set FullFileName to '\' + + 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 + +#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 + +// +// 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 +#include + +#define LINE_SIZE 511 +#define BUFFER_LINES 50 + + +#ifdef NWDBG + +#include // 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 + + +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 +#include + +#include + +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 + +#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 +#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\ 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 + // + + 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 + +#include + +#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 , 09-Jan-1994 + +--*/ + +#include "procs.h" +#include "nodetype.h" + +#include +#include +#include + +// +// 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("\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("\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("\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("\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("\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( "\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( "\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("\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( "" ); + 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 : \n" ); + } + + b = GET_STRING( Buffer, Logon.PassWord ); + if ( b ) { + printf( "Password : %ws\n", Buffer ); + } else { + printf( "Password : \n" ); + } + + b = GET_STRING( Buffer, Logon.ServerName ); + if ( b ) { + printf( "Pref Server : %ws\n", Buffer ); + } else { + printf( "Pref Server : \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( "\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 : \n" ); + } + + b = GET_STRING( Buffer, Context.CurrentContext ); + if ( b ) { + printf( "Current Context : %ws\n", Buffer ); + } else { + printf( "Current Context :\n" ); + } + + if ( Context.Credential != NULL ) { + + printf( "--------------------- Credential Data ----------------------\n"); + + b = getmem( (PVOID)Context.Credential, &Credential, sizeof(NDS_CREDENTIAL), NULL ); + if (!b ) { + printf( "\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( "\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( "\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( "\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("<<>>\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("\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("\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("<<>>\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("<<>>\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("<<>>\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( "\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", "" ); + + 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("<<>>\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("<<>>\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("<<>>\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("<<>>\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("<<>>\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 + +#include + +#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 + +#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 + +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 + +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 + +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 + +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 + +typedef struct { + + DWORD verb; + UINT count; + char *bufEnd; + PVOID nextItem; + +} NDS_TAG, *PNDS_TAG; + +#include + +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 + +#include + +#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-. 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 + +#else + + #include + #include + #include + #include + +#endif + +#include +#include +#include +#include +#include + +// 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 +#include "Const.h" +#include "Nodetype.h" +#include "ncp.h" +#include "Struct.h" +#include "Data.h" +#include "Exchange.h" +#include + +// +// NDS Additions. +// + +#include +#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 // 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 + +typedef struct _MISSING_DATA_ENTRY { + ULONG DataOffset; + USHORT ByteCount; +} MISSING_DATA_ENTRY, *PMISSING_DATA_ENTRY; + +#include + +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 + +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: + + + +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 + +// +// 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 -- cgit v1.2.3