summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/fsctl.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/fsctl.c')
-rw-r--r--private/nw/rdr/fsctl.c5930
1 files changed, 5930 insertions, 0 deletions
diff --git a/private/nw/rdr/fsctl.c b/private/nw/rdr/fsctl.c
new file mode 100644
index 000000000..159cd1385
--- /dev/null
+++ b/private/nw/rdr/fsctl.c
@@ -0,0 +1,5930 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ FsCtrl.c
+
+Abstract:
+
+ This module implements the File System Control routines for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 29-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include "ntddrdr.h"
+#include "ntddmup.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FSCTRL)
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonDeviceIoControl (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+StartRedirector(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+StopRedirector(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BindToTransport (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ChangePassword (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetInfo (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetDebug (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetMessage (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetStats (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetPrintJobId (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnectionDetails(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnectionPerformance(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+RegisterWithMup(
+ VOID
+ );
+
+VOID
+DeregisterWithMup(
+ VOID
+ );
+
+NTSTATUS
+QueryPath (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UserNcp(
+ ULONG Function,
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UserNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+FspCompleteLogin(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnection(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+EnumConnections(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+DeleteConnection(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteNetResourceEntry(
+ IN OUT PCHAR *FixedPortion,
+ IN OUT PWCHAR *EndOfVariableData,
+ IN PUNICODE_STRING ContainerName OPTIONAL,
+ IN PUNICODE_STRING LocalName OPTIONAL,
+ IN PUNICODE_STRING RemoteName,
+ IN ULONG ScopeFlag,
+ IN ULONG DisplayFlag,
+ IN ULONG UsageFlag,
+ IN ULONG ShareType,
+ OUT PULONG EntrySize
+ );
+
+BOOL
+CopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ );
+
+NTSTATUS
+GetRemoteHandle(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetUserName(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetChallenge(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteConnStatusEntry(
+ PSCB pConnectionScb,
+ PBYTE pbUserBuffer,
+ DWORD dwBufferLen,
+ DWORD *pdwBytesWritten,
+ DWORD *pdwBytesNeeded,
+ BOOLEAN fCallerScb
+ );
+
+NTSTATUS
+GetConnStatus(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ );
+
+NTSTATUS
+GetConnectionInfo(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetPreferredServer(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetShareBit(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ );
+
+//
+// Statics
+//
+
+HANDLE MupHandle;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdFileSystemControl )
+#pragma alloc_text( PAGE, NwCommonFileSystemControl )
+#pragma alloc_text( PAGE, NwFsdDeviceIoControl )
+#pragma alloc_text( PAGE, NwCommonDeviceIoControl )
+#pragma alloc_text( PAGE, BindToTransport )
+#pragma alloc_text( PAGE, ChangePassword )
+#pragma alloc_text( PAGE, SetInfo )
+#pragma alloc_text( PAGE, GetStats )
+#pragma alloc_text( PAGE, GetPrintJobId )
+#pragma alloc_text( PAGE, StartRedirector )
+#pragma alloc_text( PAGE, StopRedirector )
+#pragma alloc_text( PAGE, RegisterWithMup )
+#pragma alloc_text( PAGE, DeregisterWithMup )
+#pragma alloc_text( PAGE, QueryPath )
+#pragma alloc_text( PAGE, UserNcp )
+#pragma alloc_text( PAGE, GetConnection )
+#pragma alloc_text( PAGE, DeleteConnection )
+#pragma alloc_text( PAGE, WriteNetResourceEntry )
+#pragma alloc_text( PAGE, CopyStringToBuffer )
+#pragma alloc_text( PAGE, GetRemoteHandle )
+#pragma alloc_text( PAGE, GetUserName )
+#pragma alloc_text( PAGE, GetChallenge )
+#pragma alloc_text( PAGE, WriteConnStatusEntry )
+#pragma alloc_text( PAGE, GetConnStatus )
+#pragma alloc_text( PAGE, GetConnectionInfo )
+#pragma alloc_text( PAGE, GetPreferredServer )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, UserNcpCallback )
+#pragma alloc_text( PAGE1, GetConnectionDetails )
+#pragma alloc_text( PAGE1, GetMessage )
+#pragma alloc_text( PAGE1, EnumConnections )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+NTSTATUS
+NwFsdFileSystemControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of FileSystem control operations
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdFileSystemControl\n", 0);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+ Status = NwCommonFileSystemControl( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdFileSystemControl -> %08lx\n", Status);
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonFileSystemControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for doing FileSystem control operations called
+ by both the fsd and fsp threads
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ PIRP Irp;
+ ULONG Function;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ try {
+
+ //
+ // Get a pointer to the current Irp stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ Function = IrpSp->Parameters.FileSystemControl.FsControlCode;
+
+ DebugTrace(+1, Dbg, "NwCommonFileSystemControl\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
+ DebugTrace( 0, Dbg, "Function = %08lx\n", Function);
+ DebugTrace( 0, Dbg, "Function = %d\n", (Function >> 2) & 0x0fff);
+
+ //
+ // We know this is a file system control so we'll case on the
+ // minor function, and call a internal worker routine to complete
+ // the irp.
+ //
+
+ if (IrpSp->MinorFunction != IRP_MN_USER_FS_REQUEST ) {
+ DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ switch (Function) {
+
+ case FSCTL_NWR_START:
+ Status = StartRedirector( IrpContext );
+ break;
+
+ case FSCTL_NWR_STOP:
+ Status = StopRedirector( IrpContext );
+ break;
+
+ case FSCTL_NWR_LOGON:
+ Status = Logon( IrpContext );
+ break;
+
+ case FSCTL_NWR_LOGOFF:
+ Status = Logoff( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONNECTION:
+ Status = GetConnection( IrpContext );
+ break;
+
+ case FSCTL_NWR_ENUMERATE_CONNECTIONS:
+ Status = EnumConnections( IrpContext );
+ break;
+
+ case FSCTL_NWR_DELETE_CONNECTION:
+ Status = DeleteConnection( IrpContext );
+ break;
+
+ case FSCTL_NWR_BIND_TO_TRANSPORT:
+ Status = BindToTransport( IrpContext );
+ break;
+
+ case FSCTL_NWR_CHANGE_PASS:
+ Status = ChangePassword( IrpContext );
+ break;
+
+ case FSCTL_NWR_SET_INFO:
+ Status = SetInfo( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_DETAILS:
+ Status = GetConnectionDetails( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_MESSAGE:
+ Status = GetMessage( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_STATISTICS:
+ Status = GetStats( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_USERNAME:
+ Status = GetUserName( IrpContext );
+ break;
+
+ case FSCTL_NWR_CHALLENGE:
+ Status = GetChallenge( IrpContext );
+ break;
+
+ case FSCTL_GET_PRINT_ID:
+ Status = GetPrintJobId( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_STATUS:
+ Status = GetConnStatus( IrpContext, IrpSp->FileObject );
+ break;
+
+ case FSCTL_NWR_GET_CONN_INFO:
+ Status = GetConnectionInfo( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_PREFERRED_SERVER:
+ Status = GetPreferredServer( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_PERFORMANCE:
+ Status = GetConnectionPerformance( IrpContext );
+ break;
+
+ case FSCTL_NWR_SET_SHAREBIT:
+ Status = SetShareBit( IrpContext, IrpSp->FileObject );
+ break;
+
+ default:
+
+ if (( Function >= NWR_ANY_NCP(0)) &&
+ ( Function <= NWR_ANY_HANDLE_NCP(0x00ff))) {
+
+ Status = UserNcp( Function, IrpContext );
+ break;
+
+ }
+
+ if (( Function >= NWR_ANY_NDS(0)) &&
+ ( Function <= NWR_ANY_NDS(0x00ff))) {
+
+ Status = DispatchNds( Function, IrpContext );
+ break;
+ }
+
+ DebugTrace( 0, Dbg, "Invalid FS Control Code %08lx\n",
+ IrpSp->Parameters.FileSystemControl.FsControlCode);
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ }
+
+ } finally {
+
+ NwDereferenceUnlockableCodeSection ();
+
+ DebugTrace(-1, Dbg, "NwCommonFileSystemControl -> %08lx\n", Status);
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NwFsdDeviceIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of DeviceIoControl file operations
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdDeviceIoControl\n", 0);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+ Status = NwCommonDeviceIoControl( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest(IrpContext, Status);
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdDeviceIoControl -> %08lx\n", Status);
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonDeviceIoControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for doing FileSystem control operations called
+ by both the fsd and fsp threads
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ PIRP Irp;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ try {
+
+ //
+ // Get a pointer to the current Irp stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonDeviceIoControl\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
+ DebugTrace( 0, Dbg, "Function = %08lx\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ //
+ // We know this is a DeviceIoControl so we'll case on the
+ // minor function, and call a internal worker routine to complete
+ // the irp.
+ //
+
+ switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+
+ case IOCTL_REDIR_QUERY_PATH:
+ Status = QueryPath( IrpContext );
+ break;
+
+ case IOCTL_NWR_RAW_HANDLE:
+ Status = GetRemoteHandle( IrpContext );
+ break;
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid IO Control Code %08lx\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ } finally {
+
+ NwDereferenceUnlockableCodeSection ();
+ DebugTrace(-1, Dbg, "NwCommonDeviceIoControl -> %08lx\n", Status);
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+BindToTransport (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine records the name of the transport to be used and
+ initialises the PermanentScb.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ PAGED_CODE();
+
+#ifdef _PNP_POWER
+
+ //
+ // For PnP builds, register the bind handlers.
+ //
+
+ DebugTrace( 0 , Dbg, "Register TDI bind handlers.\n", 0 );
+
+ TdiInitialize();
+
+ return TdiRegisterNotificationHandler( HandleTdiBindMessage,
+ HandleTdiUnbindMessage,
+ &TdiBindingHandle );
+
+#endif
+
+ DebugTrace(+1, Dbg, "Bind to transport\n", 0);
+
+ try {
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ try_return( Status );
+ }
+
+ if (IpxHandle != NULL) {
+
+ //
+ // Can only bind to one transport at a time in this implementation
+ //
+
+ try_return(Status= STATUS_SHARING_VIOLATION);
+ }
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.Bind.TransportName)) +
+ InputBuffer->Parameters.Bind.TransportNameLength) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if ( IpxTransportName.Buffer != NULL ) {
+ FREE_POOL( IpxTransportName.Buffer );
+ }
+
+ Status = SetUnicodeString ( &IpxTransportName,
+ InputBuffer->Parameters.Bind.TransportNameLength,
+ InputBuffer->Parameters.Bind.TransportName);
+
+ DebugTrace(-1, Dbg, "\"%wZ\"\n", &IpxTransportName);
+
+ if ( !NT_SUCCESS(Status) ) {
+ try_return(Status);
+ }
+
+ Status = IpxOpen();
+ if ( !NT_SUCCESS(Status) ) {
+ try_return(Status);
+ }
+
+ //
+ // Verify that have a large enough stack size.
+ //
+
+ if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) {
+ IpxClose();
+ try_return( Status = STATUS_INVALID_PARAMETER );
+ }
+
+#ifndef QFE_BUILD
+
+ //
+ // Submit a line change request.
+ //
+
+ SubmitLineChangeRequest();
+#endif
+
+ //
+ // Open a handle to IPX.
+ //
+
+ NwPermanentNpScb.Server.Socket = 0;
+ Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = SetEventHandler (
+ IrpContext,
+ &NwPermanentNpScb.Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ &NwPermanentNpScb );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ IrpContext->pNpScb = &NwPermanentNpScb;
+
+ NwRcb.State = RCB_STATE_RUNNING;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Bind to transport\n", 0);
+ return Status;
+
+}
+
+#ifdef _PNP_POWER
+
+VOID
+HandleTdiBindMessage(
+ IN PUNICODE_STRING DeviceName
+)
+/*+++
+
+Description: This function is the bind handler for NetPnP
+ support. This function is registered with TDI and is called
+ whenever a transport starts up or stops. We watch for IPX
+ coming and going and do the appropriate thing.
+
+ See also: HandleTdiUnbindMessage()
+
+---*/
+{
+
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ PIRP pIrp = NULL;
+
+ PAGED_CODE();
+
+ //
+ // See if this is IPX requesting a bind. We only bind to NwLnkIpx.
+ //
+
+ if ( !RtlEqualUnicodeString( &TdiIpxDeviceName, DeviceName, TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Ignoring PnP Bind request for %wZ\n", DeviceName );
+ return;
+ }
+
+ //
+ // Make sure we aren't already bound.
+ //
+
+ if ( ( NwRcb.State != RCB_STATE_NEED_BIND ) ||
+ ( IpxHandle != NULL ) ) {
+
+ DebugTrace( 0, Dbg, "Discarding duplicate PnP bind request.\n", 0 );
+ return;
+ }
+
+ ASSERT( IpxTransportName.Buffer == NULL );
+ ASSERT( pIpxDeviceObject == NULL );
+
+ Status = DuplicateUnicodeStringWithString ( &IpxTransportName,
+ DeviceName,
+ PagedPool );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Failing IPX bind: Can't set device name.\n", 0 );
+ return;
+ }
+
+ //
+ // Open IPX.
+ //
+
+ Status = IpxOpen();
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that have a large enough stack size.
+ //
+
+ if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Submit a line change request.
+ //
+
+ SubmitLineChangeRequest();
+
+ //
+ // Allocate an irp and irp context. AllocateIrpContext may raise status.
+ //
+
+ pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE );
+
+ if ( pIrp == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ IrpContext = AllocateIrpContext( pIrp );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( IrpContext != NULL );
+
+ //
+ // Open a handle to IPX for the permanent scb.
+ //
+
+ NwPermanentNpScb.Server.Socket = 0;
+ Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = SetEventHandler (
+ IrpContext,
+ &NwPermanentNpScb.Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ &NwPermanentNpScb );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ IrpContext->pNpScb = &NwPermanentNpScb;
+
+ NwRcb.State = RCB_STATE_RUNNING;
+
+ DebugTrace( 0, Dbg, "Opened IPX for NwRdr.\n", 0 );
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we failed, clean up our globals.
+ //
+
+ if ( pIpxDeviceObject != NULL ) {
+ IpxClose();
+ pIpxDeviceObject = NULL;
+ }
+
+ IpxHandle = NULL;
+
+ if ( IpxTransportName.Buffer != NULL ) {
+ FREE_POOL( IpxTransportName.Buffer );
+ IpxTransportName.Buffer = NULL;
+ }
+
+ DebugTrace( 0, Dbg, "Failing IPX bind request.\n", 0 );
+
+ }
+
+ if ( pIrp != NULL ) {
+ FREE_IRP( pIrp );
+ }
+
+ if ( IrpContext != NULL ) {
+ FreeIrpContext( IrpContext );
+ }
+
+ return;
+
+}
+
+VOID
+HandleTdiUnbindMessage(
+ IN PUNICODE_STRING DeviceName
+)
+/*+++
+
+Description: This function is the unbind handler for NetPnP
+ support. This function is registered with TDI and is called
+ whenever a transport stops. We watch for IPX coming and going
+ and do the appropriate thing.
+
+ See also: HandleTdiBindMessage()
+
+---*/
+{
+
+ DebugTrace( 0, Dbg, "TDI unbind request ignored. Not Supported.\n", 0 );
+ return;
+
+}
+
+#endif
+
+
+NTSTATUS
+ChangePassword (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine records a change in the user's cached password.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER Uid;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "change password\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.ChangePass.UserName)) +
+ InputBuffer->Parameters.ChangePass.UserNameLength +
+ InputBuffer->Parameters.ChangePass.PasswordLength +
+ InputBuffer->Parameters.ChangePass.ServerNameLength ) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Get local pointer to the fsctl parameters
+ //
+
+ UserName.Buffer = InputBuffer->Parameters.ChangePass.UserName;
+ UserName.Length = (USHORT)InputBuffer->Parameters.ChangePass.UserNameLength;
+
+ Password.Buffer = UserName.Buffer +
+ (InputBuffer->Parameters.ChangePass.UserNameLength / 2);
+ Password.Length = (USHORT)InputBuffer->Parameters.ChangePass.PasswordLength;
+
+ ServerName.Buffer = Password.Buffer +
+ (InputBuffer->Parameters.ChangePass.PasswordLength / 2);
+ ServerName.Length = (USHORT)InputBuffer->Parameters.ChangePass.ServerNameLength;
+
+ //
+ // Update the default password for this user
+ //
+
+ Status = UpdateUsersPassword( &UserName, &Password, &Uid );
+
+ //
+ // Update the default password for this user
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ UpdateServerPassword( IrpContext, &ServerName, &UserName, &Password, &Uid );
+ }
+
+ Status = STATUS_SUCCESS;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Change Password\n", 0);
+ return Status;
+}
+
+
+NTSTATUS
+SetInfo (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine set netware redirector parameters.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Set info\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.SetInfo.PreferredServer)) +
+ InputBuffer->Parameters.SetInfo.PreferredServerLength +
+ InputBuffer->Parameters.SetInfo.ProviderNameLength ) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We don't do anything with a preferred server change, but if we
+ // get a request to change the preferred tree and context, we
+ // validate the context. The rest of the changes happen at the next
+ // login.
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.PreferredServerLength > 0 &&
+ InputBuffer->Parameters.SetInfo.PreferredServer[0] == '*' ) {
+
+ UNICODE_STRING Tree, NewContext;
+ USHORT i = 0;
+
+ //
+ // Dig out the tree name. Skip over the *.
+ //
+
+ Tree.Length = 0;
+ Tree.Buffer = InputBuffer->Parameters.SetInfo.PreferredServer + 1;
+
+ while ( i < InputBuffer->Parameters.SetInfo.PreferredServerLength ) {
+
+ if ( InputBuffer->Parameters.SetInfo.PreferredServer[i] == L'\\' ) {
+
+ i++;
+ Tree.Length -= sizeof( WCHAR );
+ Tree.MaximumLength = Tree.Length;
+ break;
+
+ } else {
+
+ Tree.Length += sizeof( WCHAR );
+ i++;
+
+ }
+ }
+
+ DebugTrace( 0, Dbg, "Tree: %wZ\n", &Tree );
+
+ NewContext.Length = (USHORT)InputBuffer->Parameters.SetInfo.PreferredServerLength -
+ ( Tree.Length + (2 * sizeof( WCHAR ) ) );
+ NewContext.Buffer = &InputBuffer->Parameters.SetInfo.PreferredServer[i];
+ NewContext.MaximumLength = NewContext.Length;
+
+ //
+ // Strip off any leading period.
+ //
+
+ if ( NewContext.Buffer[0] == L'.' ) {
+
+ NewContext.Buffer++;
+ NewContext.Length -= sizeof( WCHAR );
+ NewContext.MaximumLength -= sizeof( WCHAR );
+
+ }
+
+ DebugTrace( 0, Dbg, "Context: %wZ\n", &NewContext );
+
+ Status = NdsVerifyContext( IrpContext, &Tree, &NewContext );
+
+ if ( !NT_SUCCESS( Status )) {
+ try_return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ //
+ // Next set the provider name string.
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.ProviderNameLength != 0 ) {
+
+ PWCH TempBuffer;
+
+ TempBuffer = ALLOCATE_POOL_EX( PagedPool, InputBuffer->Parameters.SetInfo.ProviderNameLength );
+
+ if ( NwProviderName.Buffer != NULL ) {
+ FREE_POOL( NwProviderName.Buffer );
+ }
+
+ NwProviderName.Buffer = TempBuffer;
+ NwProviderName.Length = (USHORT)InputBuffer->Parameters.SetInfo.ProviderNameLength;
+
+ RtlCopyMemory(
+ NwProviderName.Buffer,
+ (PUCHAR)InputBuffer->Parameters.SetInfo.PreferredServer +
+ InputBuffer->Parameters.SetInfo.PreferredServerLength,
+ NwProviderName.Length );
+
+ }
+
+ //
+ // Set burst mode parameters
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize == 0 ) {
+ NwBurstModeEnabled = FALSE;
+ } else if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize != -1 ) {
+ NwBurstModeEnabled = TRUE;
+ NwMaxSendSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize;
+ NwMaxReceiveSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize;
+ }
+
+ //
+ // Set print options
+ //
+
+ NwPrintOptions = InputBuffer->Parameters.SetInfo.PrintOption;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Set info\n", 0);
+ return Status;
+}
+
+
+NTSTATUS
+GetMessage (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine queues an IRP to a list of IRP Contexts available for
+ reading server administrative messages.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PVOID OutputBuffer;
+
+ DebugTrace(+1, Dbg, "GetMessage\n", 0);
+
+ NwLockUserBuffer( Irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ IrpContext->pOriginalMdlAddress = Irp->MdlAddress;
+
+ IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer;
+ IrpContext->Specific.FileSystemControl.Length = OutputBufferLength;
+
+ ExInterlockedInsertTailList(
+ &NwGetMessageList,
+ &IrpContext->NextRequest,
+ &NwMessageSpinLock );
+
+ IoMarkIrpPending( Irp );
+
+ //
+ // Set the cancel routine.
+ //
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+
+ if ( Irp->Cancel ) {
+ NwCancelIrp( NULL, Irp );
+ } else {
+ IoSetCancelRoutine( Irp, NwCancelIrp );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+ }
+
+ DebugTrace(-1, Dbg, "Get Message -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetStats (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies Stats into the users buffer.
+
+ Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PVOID OutputBuffer;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetStats\n", 0);
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ if (NwRcb.State != RCB_STATE_RUNNING) {
+
+ Status = STATUS_REDIRECTOR_NOT_STARTED;
+
+ } else if (OutputBufferLength < sizeof(NW_REDIR_STATISTICS)) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if (OutputBufferLength != sizeof(NW_REDIR_STATISTICS)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ Stats.CurrentCommands = ContextCount;
+
+ RtlCopyMemory(OutputBuffer, &Stats, OutputBufferLength);
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = OutputBufferLength;
+
+ }
+
+ DebugTrace(-1, Dbg, "GetStats -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetPrintJobId (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the Job ID for this job.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PQUERY_PRINT_JOB_INFO OutputBuffer;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE NodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetJobId\n", 0);
+
+ NodeTypeCode = NwDecodeFileObject(
+ IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb );
+
+ if (NodeTypeCode != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( OutputBufferLength < sizeof( QUERY_PRINT_JOB_INFO ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ OutputBuffer->JobId = Icb->JobId;
+ //OutputBuffer->ServerName = BUGBUG
+ //OutputBuffer->QueueName = BUGBUG
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetJobId -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetConnectionDetails(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the details for a connection. This is normally used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_CONNECTION_DETAILS OutputBuffer;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetConnectionDetails\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB_SCB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status );
+
+ return Status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ pScb = (PSCB)Icb->SuperType.Scb;
+ nodeTypeCode = pScb->NodeTypeCode;
+
+ if (nodeTypeCode != NW_NTC_SCB) {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ pNpScb = pScb->pNpScb;
+
+ if ( OutputBufferLength < sizeof( NWR_GET_CONNECTION_DETAILS ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNextNpScb;
+ UCHAR OrderNumber;
+ OEM_STRING ServerName;
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ for ( ScbQueueEntry = ScbQueue.Flink, OrderNumber = 1;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = ScbQueueEntry->Flink, OrderNumber++ ) {
+
+ pNextNpScb = CONTAINING_RECORD(
+ ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+
+ //
+ // Check to make sure that this SCB is usable.
+ //
+
+ if ( pNextNpScb == pNpScb ) {
+ break;
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ OutputBuffer->OrderNumber = OrderNumber;
+
+ RtlZeroMemory( OutputBuffer->ServerName, sizeof(OutputBuffer->ServerName));
+ ServerName.Buffer = OutputBuffer->ServerName;
+ ServerName.Length = sizeof(OutputBuffer->ServerName);
+ ServerName.MaximumLength = sizeof(OutputBuffer->ServerName);
+ RtlUpcaseUnicodeStringToCountedOemString( &ServerName, &pNpScb->ServerName, FALSE);
+
+ RtlCopyMemory( OutputBuffer->ServerAddress,
+ &pNpScb->ServerAddress,
+ sizeof(OutputBuffer->ServerAddress) );
+
+ OutputBuffer->ServerAddress[12];
+ OutputBuffer->ConnectionNumberLo = pNpScb->ConnectionNo;
+ OutputBuffer->ConnectionNumberHi = pNpScb->ConnectionNoHigh;
+ // BUGBUG We need to ask the server during connect!
+ OutputBuffer->MajorVersion = 1;
+ OutputBuffer->MinorVersion = 11;
+ OutputBuffer->Preferred = pScb->PreferredServer;
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status );
+ return Status;
+}
+
+#if 0
+
+NTSTATUS
+GetOurAddress(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the value of OurAddress. This is normally used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_OUR_ADDRESS OutputBuffer;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ DebugTrace(+1, Dbg, "GetOurAddress\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB_SCB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status );
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ if ( OutputBufferLength < sizeof( NWR_GET_OUR_ADDRESS ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ RtlCopyMemory( OutputBuffer->Address,
+ &OurAddress,
+ sizeof(OurAddress );
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status );
+ return Status;
+}
+#endif
+
+
+NTSTATUS
+StartRedirector(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine starts the redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // We need to be in the FSP to Register the MUP.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return( Status );
+ }
+
+ NwRcb.State = RCB_STATE_STARTING;
+
+ FspProcess = PsGetCurrentProcess();
+
+#ifdef QFE_BUILD
+ StartTimer() ;
+#endif
+
+ //
+ // Now connect to the MUP.
+ //
+
+ RegisterWithMup();
+
+ KeQuerySystemTime( &Stats.StatisticsStartTime );
+
+ NwRcb.State = RCB_STATE_NEED_BIND;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+StopRedirector(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine shuts down the redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY LogonListEntry;
+ ULONG ActiveHandles;
+ ULONG RcbOpenCount;
+
+ PAGED_CODE();
+
+ //
+ // We need to be in the FSP to Deregister the MUP.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return( Status );
+ }
+
+#ifdef _PNP_POWER
+
+ //
+ // Unregister the bind handler with tdi.
+ //
+
+ if ( TdiBindingHandle != NULL ) {
+ TdiDeregisterNotificationHandler( TdiBindingHandle );
+ TdiBindingHandle = NULL;
+ }
+
+#endif
+
+ NwRcb.State = RCB_STATE_SHUTDOWN;
+
+ //
+ // Invalid all ICBs
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_SEND_ALWAYS );
+ ActiveHandles = NwInvalidateAllHandles(NULL, IrpContext);
+
+ //
+ // To expedite shutdown, set retry count down to 2.
+ //
+
+ DefaultRetryCount = 2;
+
+ //
+ // Close all VCBs
+ //
+
+ NwCloseAllVcbs( IrpContext );
+
+ //
+ // Logoff and disconnect from all servers.
+ //
+
+ NwLogoffAllServers( IrpContext, NULL );
+
+ while ( !IsListEmpty( &LogonList ) ) {
+
+ LogonListEntry = RemoveHeadList( &LogonList );
+
+ FreeLogon(CONTAINING_RECORD( LogonListEntry, LOGON, Next ));
+ }
+
+ InsertTailList( &LogonList, &Guest.Next ); // just in-case we don't unload.
+
+ StopTimer();
+
+ IpxClose();
+
+ //
+ // Remember the open count before calling DeristerWithMup since this
+ // will asynchronously cause handle count to get decremented.
+ //
+
+ RcbOpenCount = NwRcb.OpenCount;
+
+ DeregisterWithMup( );
+
+ DebugTrace(0, Dbg, "StopRedirector: Active handle count = %d\n", ActiveHandles );
+
+ //
+ // On shutdown, we need 0 remote handles and 2 open handles to
+ // the redir (one for the service, and one for the MUP) and the timer stopped.
+ //
+
+ if ( ActiveHandles == 0 && RcbOpenCount <= 2 ) {
+ return( STATUS_SUCCESS );
+ } else {
+ return( STATUS_REDIRECTOR_HAS_OPEN_HANDLES );
+ }
+}
+
+
+NTSTATUS
+RegisterWithMup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine register this redirector as a UNC provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING RdrName;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &RdrName, DD_NWFS_DEVICE_NAME_U );
+ Status = FsRtlRegisterUncProvider(
+ &MupHandle,
+ &RdrName,
+ FALSE // Do not support mailslots
+ );
+
+ return( Status );
+}
+
+
+
+VOID
+DeregisterWithMup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine deregisters this redirector as a UNC provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ FsRtlDeregisterUncProvider( MupHandle );
+}
+
+
+NTSTATUS
+QueryPath(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies whether a path is a netware path.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PQUERY_PATH_REQUEST qpRequest;
+ PQUERY_PATH_RESPONSE qpResponse;
+ UNICODE_STRING FilePathName;
+ ULONG OutputBufferLength;
+ ULONG InputBufferLength;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+
+ UNICODE_STRING DriveName;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING VolumeName;
+ UNICODE_STRING PathName;
+ UNICODE_STRING FileName;
+ UNICODE_STRING UnicodeUid;
+ WCHAR DriveLetter;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ ASSERT (( IOCTL_REDIR_QUERY_PATH & 3) == METHOD_NEITHER);
+
+ RtlInitUnicodeString( &UnicodeUid, NULL );
+
+ try {
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ //
+ // The input buffer is either in Irp->AssociatedIrp.SystemBuffer, or
+ // in the Type3InputBuffer for type 3 IRP's.
+ //
+
+ qpRequest = (PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+ qpResponse = (PQUERY_PATH_RESPONSE)qpRequest;
+
+ ASSERT( qpRequest != NULL );
+
+ FilePathName.Buffer = qpRequest->FilePathName;
+ FilePathName.Length = (USHORT)qpRequest->PathNameLength;
+
+ status = CrackPath( &FilePathName, &DriveName, &DriveLetter, &ServerName, &VolumeName, &PathName, &FileName, NULL );
+
+ if (( !NT_SUCCESS( status ) ) ||
+ ( ServerName.Length == 0 )) {
+
+ try_return( status = STATUS_BAD_NETWORK_PATH );
+ }
+
+ qpResponse->LengthAccepted = VolumeName.Length;
+
+ //
+ // As far as the redirector is concerned, QueryPath is a form
+ // of create. Set up the IrpContext appropriately.
+ //
+
+ IrpContext->Specific.Create.VolumeName = VolumeName;
+ IrpContext->Specific.Create.PathName = PathName;
+ IrpContext->Specific.Create.DriveLetter = DriveLetter;
+ IrpContext->Specific.Create.FullPathName = FilePathName;
+
+ RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL );
+
+ //
+ // The irp context specific data is now zeroed out by AllocateIrpContext,
+ // so we don't have to worry about re-setting the specific data here.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+
+ IrpContext->Specific.Create.UserUid = GetUid( &SubjectContext );
+
+ SeReleaseSubjectContext(&SubjectContext);
+
+ try {
+
+ //
+ // The slightly more complicated approach. This function
+ // handles the resolution of the server/volume duple. It
+ // may use the bindery, cached nds information, or fresh
+ // nds information.
+ //
+
+ status = HandleVolumeAttach( IrpContext,
+ &ServerName,
+ &VolumeName );
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+ status = STATUS_BAD_NETWORK_PATH;
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ RtlFreeUnicodeString(&UnicodeUid);
+ }
+
+ return( status );
+}
+
+NTSTATUS
+UserNcp(
+ ULONG IoctlCode,
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges an NCP with the server.
+
+ BUGBUG - We need to filter or security check what the user is
+ doing.
+
+Arguments:
+
+ IoctlCode - Supplies the code to be used for the NCP.
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+ PCHAR InputBuffer;
+ ULONG InputBufferLength;
+
+ PICB icb;
+ PSCB pScb;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ UCHAR Function = ANY_NCP_OPCODE( IoctlCode );
+ UCHAR Subfunction = 0;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ DebugTrace(+1, DEBUG_TRACE_USERNCP, "UserNcp...\n", 0);
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "irp = %08lx\n", (ULONG)irp);
+
+ //
+ // This F2 and ANY NCP must be addressed either to \Device\NwRdr or
+ // \Device\NwRdr\<servername> any additional name is not allowed.
+ // If the handle used for the Irp specifies \Device\NwRdr then the
+ // redirector gets to choose among the connected servers.
+ //
+ // For HANDLE NCP the file must be an FCB.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ if ((nodeTypeCode == NW_NTC_ICB_SCB) &&
+ (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode))) {
+
+ // All ok
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ pScb = (PSCB)icb->SuperType.Scb;
+ nodeTypeCode = pScb->NodeTypeCode;
+
+ IrpContext->pScb = pScb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ } else if (nodeTypeCode == NW_NTC_ICB) {
+
+ if ((IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) &&
+ (InputBufferLength < 7)) {
+
+ // Buffer needs enough space for the handle!
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Not enough space for handle %x\n", InputBufferLength);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ // Let through FCB's and DCB's
+ //
+
+ NwVerifyIcb( icb );
+
+ pScb = (PSCB)icb->SuperType.Fcb->Scb;
+ nodeTypeCode = icb->SuperType.Fcb->NodeTypeCode;
+
+ IrpContext->pScb = pScb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ //
+ // Set the icb pointer in case the cache gets
+ // flushed because the write routines look at it.
+ //
+
+ IrpContext->Icb = icb;
+ AcquireFcbAndFlushCache( IrpContext, icb->NpFcb );
+
+ } else {
+
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", irpSp->FileObject);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+ }
+
+ if (icb->Pid == INVALID_PID) {
+ status = NwMapPid( (ULONG)PsGetCurrentThread(), &icb->Pid );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp Pid = %02lx\n", icb->Pid );
+ NwSetEndOfJobRequired(icb->Pid);
+
+ }
+
+ //
+ // We now know where to send the NCP. Lock down the users buffers and
+ // build the Mdls required to transfer the data.
+ //
+
+ InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ if ( OutputBufferLength ) {
+ NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
+ } else {
+ OutputBuffer = NULL;
+ }
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ if (InputBufferLength != 0) {
+ if (IS_IT_NWR_ANY_NCP(IoctlCode)) {
+ Subfunction = InputBuffer[0];
+ } else if (InputBufferLength >= 3) {
+ Subfunction = InputBuffer[2];
+ }
+ }
+
+
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "UserNcp function = %x\n", Function );
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, " & Subfunction = %x\n", Subfunction );
+ dump( DEBUG_TRACE_USERNCP, InputBuffer, InputBufferLength );
+ //dump( DEBUG_TRACE_USERNCP, OutputBuffer, OutputBufferLength );
+
+ if ((Function == NCP_ADMIN_FUNCTION ) &&
+ (InputBufferLength >= 4 )) {
+
+ if ( ( (Subfunction == NCP_SUBFUNC_79) ||
+ (Subfunction == NCP_CREATE_QUEUE_JOB ) ) &&
+ icb->HasRemoteHandle) {
+
+ //
+ // Trying to create a job on a queue that already has a job
+ // on it. Cancel the old job.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB, // Close File And Cancel Queue Job
+ icb->SuperType.Fcb->Vcb->Specific.Print.QueueId,
+ icb->JobId );
+
+ if (!NT_SUCCESS(status)) {
+
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "DeleteOldJob got status -> %08lx\n", status );
+ // Don't worry if the delete fails, proceed with the create
+ }
+
+ icb->IsPrintJob = FALSE; // App will have to queue or cancel job, not rdr
+
+ } else if ((Subfunction == NCP_PLAIN_TEXT_LOGIN ) ||
+ (Subfunction == NCP_ENCRYPTED_LOGIN )) {
+
+ UNICODE_STRING UserName;
+ OEM_STRING OemUserName;
+ PUCHAR InputBuffer;
+
+ //
+ // Trying to do a login.
+ //
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Assume success, store the user name in the SCB.
+ //
+
+ try {
+
+ InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ OemUserName.Length = InputBuffer[ 13 ];
+ OemUserName.Buffer = &InputBuffer[14];
+
+ UserName.MaximumLength = OemUserName.Length * sizeof(WCHAR);
+ if ( OemUserName.Length == 0 || OemUserName.Length > MAX_USER_NAME_LENGTH ) {
+ try_return( status = STATUS_NO_SUCH_USER );
+ }
+
+ UserName.Buffer = ALLOCATE_POOL_EX( NonPagedPool, UserName.MaximumLength );
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemUserName.Length != 0 ) {
+ status = RtlOemStringToCountedUnicodeString( &UserName, &OemUserName, FALSE );
+ } else {
+ UserName.Length = 0;
+ }
+try_exit: NOTHING;
+ } finally {
+ NOTHING;
+ }
+
+ if ( NT_SUCCESS( status )) {
+
+ if ( pScb->OpenFileCount != 0 &&
+ pScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ if (!RtlEqualUnicodeString( &pScb->UserName, &UserName, TRUE )) {
+
+ //
+ // But were already logged in to this server and at
+ // least one other handle is using the connection and
+ // the user is trying to change the username.
+ //
+
+ FREE_POOL( UserName.Buffer );
+ return STATUS_NETWORK_CREDENTIAL_CONFLICT;
+
+ } else {
+
+ PUCHAR VerifyBuffer = ALLOCATE_POOL( PagedPool, InputBufferLength );
+
+ //
+ // Same username. Validate password is correct.
+
+ if (VerifyBuffer == NULL) {
+ FREE_POOL( UserName.Buffer );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( VerifyBuffer, InputBuffer, InputBufferLength );
+
+ if (IS_IT_NWR_ANY_NCP(IoctlCode)) {
+ VerifyBuffer[0] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ?
+ NCP_PLAIN_TEXT_VERIFY_PASSWORD:
+ NCP_ENCRYPTED_VERIFY_PASSWORD;
+
+ } else {
+ VerifyBuffer[2] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ?
+ NCP_PLAIN_TEXT_VERIFY_PASSWORD:
+ NCP_ENCRYPTED_VERIFY_PASSWORD;
+ }
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr",
+ Function, VerifyBuffer[0],
+ &VerifyBuffer[1], InputBufferLength - 1 );
+
+ FREE_POOL( UserName.Buffer );
+ FREE_POOL( VerifyBuffer );
+ return status;
+
+ }
+ }
+
+ if (pScb->UserName.Buffer) {
+ FREE_POOL( pScb->UserName.Buffer ); // May include space for password too.
+ }
+
+ IrpContext->pNpScb->pScb->UserName = UserName;
+ IrpContext->pNpScb->pScb->Password.Buffer = UserName.Buffer;
+ IrpContext->pNpScb->pScb->Password.Length = 0;
+
+ } else {
+ return( status );
+ }
+ }
+ } else if (Function == NCP_LOGOUT ) {
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( pScb->OpenFileCount == 0 &&
+ pScb->pNpScb->State == SCB_STATE_IN_USE &&
+ !pScb->PreferredServer ) {
+
+ NwLogoffAndDisconnect( IrpContext, pScb->pNpScb);
+ return STATUS_SUCCESS;
+
+ } else {
+
+ return(STATUS_CONNECTION_IN_USE);
+
+ }
+ }
+
+ IrpContext->Icb = icb;
+
+ //
+ // Remember where the response goes.
+ //
+
+ IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer;
+ IrpContext->Specific.FileSystemControl.Length = OutputBufferLength;
+
+ IrpContext->Specific.FileSystemControl.Function = Function;
+ IrpContext->Specific.FileSystemControl.Subfunction = Subfunction;
+
+ //
+ // Decide how to send the buffer. If it is small enough, send it
+ // by copying the user buffer to our send buffer. If it is bigger
+ // we will need to build an MDL for the user's buffer, and used a
+ // chained send.
+ //
+
+ if ( InputBufferLength == 0 ) {
+
+ // Simple request such as systime.exe
+
+ IrpContext->Specific.FileSystemControl.InputMdl = NULL;
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ "F", Function);
+
+ } else if ( InputBufferLength < MAX_SEND_DATA - sizeof( NCP_REQUEST ) - 2 ) {
+
+ //
+ // Send the request by copying it to our send buffer.
+ //
+
+ IrpContext->Specific.FileSystemControl.InputMdl = NULL;
+
+ if (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) {
+
+ //
+ // E0, E1, E2 and E3 get mapped to 14,15,16 and 17. These need
+ // a length word before the buffer.
+ //
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr",
+ Function, InputBuffer[0],
+ &InputBuffer[1], InputBufferLength - 1 );
+ } else {
+
+ //
+ // Replace the 6 bytes of InputBuffer starting at offset 1
+ // with the 6 byte NetWare address for this icb. This request
+ // is used in some of the 16 bit NCP's used for file locking.
+ // These requests are always fairly small.
+ //
+
+ if (!icb->HasRemoteHandle) {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ "Fbrr",
+ Function,
+ InputBuffer[0],
+ &icb->Handle, sizeof(icb->Handle),
+ &InputBuffer[7], InputBufferLength - 7 );
+ }
+
+ } else {
+
+ PMDL pMdl = NULL;
+
+ if (IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // We need to chain send the request. Allocate an MDL.
+ //
+
+ try {
+ pMdl = ALLOCATE_MDL(
+ &InputBuffer[1],
+ InputBufferLength - 1,
+ TRUE, // Secondary MDL
+ TRUE, // Charge quota
+ NULL );
+
+ if ( pMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmProbeAndLockPages( pMdl, irp->RequestorMode, IoReadAccess );
+
+ //
+ // Remember the MDL so we can free it.
+ //
+
+ IrpContext->Specific.FileSystemControl.InputMdl = pMdl;
+
+ //
+ // Send the request.
+ //
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sf":"Fbf",
+ Function, InputBuffer[0],
+ pMdl );
+
+
+ } finally {
+
+ if ((status != STATUS_PENDING ) &&
+ ( pMdl != NULL)) {
+
+ FREE_MDL( pMdl );
+
+ }
+ }
+ }
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+}
+
+
+
+NTSTATUS
+UserNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PIRP Irp;
+ ULONG Length;
+ PICB Icb = IrpContext->Icb;
+ PEPresponse *pResponseParameters;
+
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "UserNcpCallback...\n", 0);
+
+ if ( IrpContext->Specific.FileSystemControl.InputMdl != NULL ) {
+ MmUnlockPages( IrpContext->Specific.FileSystemControl.InputMdl );
+ FREE_MDL( IrpContext->Specific.FileSystemControl.InputMdl );
+ }
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ dump( DEBUG_TRACE_USERNCP, Response, BytesAvailable );
+
+ Buffer = IrpContext->Specific.FileSystemControl.Buffer;
+ BufferLength = IrpContext->Specific.FileSystemControl.Length;
+
+ //
+ // Get the data from the response.
+ //
+
+ Length = MIN( BufferLength, BytesAvailable - 8 );
+
+ if (IrpContext->Specific.FileSystemControl.Function == NCP_ADMIN_FUNCTION ) {
+
+ if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_79) {
+
+ //
+ // Create Queue Job and File Ncp. If the operation was a success
+ // then we need to save the handle. This will allow Write Irps
+ // on this Icb to be sent to the server.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N_r",
+ 0x3E,
+ Icb->Handle+2,4);
+
+ // Pad the handle to its full 6 bytes.
+ Icb->Handle[0] = 0;
+ Icb->Handle[1] = 0;
+
+ if (NT_SUCCESS(Status)) {
+ Icb->HasRemoteHandle = TRUE;
+ }
+
+ //
+ // Reset the file offset.
+ //
+
+ Icb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ } else if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CREATE_QUEUE_JOB ) {
+
+ //
+ // Create Queue Job and File Ncp. If the operation was a success
+ // then we need to save the handle. This will allow Write Irps
+ // on this Icb to be sent to the server.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N_r",
+ 0x2A,
+ Icb->Handle,6);
+
+ if (NT_SUCCESS(Status)) {
+ Icb->HasRemoteHandle = TRUE;
+ }
+
+ //
+ // Reset the file offset.
+ //
+
+ Icb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_7F) ||
+ (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CLOSE_FILE_AND_START_JOB )) {
+
+ // End Job request
+
+ Icb->HasRemoteHandle = FALSE;
+
+ } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_PLAIN_TEXT_LOGIN ) ||
+ (IrpContext->Specific.FileSystemControl.Subfunction == NCP_ENCRYPTED_LOGIN )) {
+
+ //
+ // Trying to do a login from a 16 bit application.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N" );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+
+ //
+ // Set the reconnect attempt flag so that we don't try to
+ // run this irp context through the reconnect logic. Doing
+ // this could deadlock the worker thread that's handling this
+ // fsp side request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+ IrpContext->PostProcessRoutine = FspCompleteLogin;
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return Status;
+
+ } else {
+ if (IrpContext->pNpScb->pScb->UserName.Buffer) {
+ FREE_POOL( IrpContext->pNpScb->pScb->UserName.Buffer );
+ }
+ RtlInitUnicodeString( &IrpContext->pNpScb->pScb->UserName, NULL);
+ RtlInitUnicodeString( &IrpContext->pNpScb->pScb->Password, NULL);
+ }
+ }
+ }
+
+ pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1);
+
+ ParseResponse( IrpContext, Response, BytesAvailable, "Nr", Buffer, Length );
+
+ Status = ( ( pResponseParameters->status &
+ ( NCP_STATUS_BAD_CONNECTION |
+ NCP_STATUS_NO_CONNECTIONS |
+ NCP_STATUS_SERVER_DOWN ) ) << 8 ) |
+ pResponseParameters->error;
+
+ if ( Status ) {
+ //
+ // Use the special error code that will cause conversion
+ // of the status back to a Dos error code to leave status and
+ // error unchanged. This is necessary because many of the
+ // NetWare error codes have different meanings depending on the
+ // operation being performed.
+ //
+
+ Status |= 0xc0010000;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ Irp->IoStatus.Information = Length;
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+FspCompleteLogin(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine reopens any Vcb directory handles.
+ It also sets the Scb as in use. This could have been done
+ in the callback routine too.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+
+ IrpContext->pNpScb->State = SCB_STATE_IN_USE;
+
+ ReconnectScb( IrpContext, IrpContext->pScb );
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+GetConnection(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the path of a connection.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_SERVER_RESOURCE OutputBuffer;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PVCB Vcb;
+ PWCH DriveName;
+ ULONG DriveNameLength;
+ UNICODE_STRING Path;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "GetConnection...\n", 0);
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Find the VCB
+ //
+
+ DriveName = InputBuffer->Parameters.GetConn.DeviceName;
+ DriveNameLength = InputBuffer->Parameters.GetConn.DeviceNameLength;
+ Vcb = NULL;
+
+ if ( DriveName[0] >= L'A' && DriveName[0] <= L'Z' &&
+ DriveName[1] == L':' &&
+ DriveNameLength == sizeof( L"X:" ) - sizeof( L'\0' ) ) {
+
+ Vcb = DriveMapTable[DriveName[0] - 'A'];
+
+ } else if ( _wcsnicmp( DriveName, L"LPT", 3 ) == 0 &&
+ DriveName[3] >= '1' && DriveName[3] <= '9' &&
+ DriveNameLength == sizeof( L"LPTX" ) - sizeof( L'\0' ) ) {
+
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveName[3] - '1'];
+ }
+
+ if ( Vcb == NULL) {
+ try_return( Status = STATUS_NO_SUCH_FILE );
+ }
+
+ OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer;
+ OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ //
+ // Calculate the VCB path to return.
+ //
+ // BUGBUG. We shouldn't have to recalc all the time. Add a
+ // new string the the VCB to remember this info. Init it
+ // in NwCreateVcb.
+ //
+
+ if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
+ Path.Buffer = Vcb->Name.Buffer + 3;
+ Path.Length = Vcb->Name.Length - 6;
+ } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
+ Path.Buffer = Vcb->Name.Buffer + 5;
+ Path.Length = Vcb->Name.Length - 10;
+ } else {
+ Path = Vcb->Name;
+ }
+
+ // Strip off the unicode prefix
+
+ Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
+ Path.Length -= Vcb->Scb->UnicodeUid.Length;
+ Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
+
+ if (OutputBufferLength < Path.Length + 2 * sizeof(WCHAR)) {
+ InputBuffer->Parameters.GetConn.BytesNeeded =
+ Path.Length + 2 * sizeof(WCHAR);
+ try_return( Status = STATUS_BUFFER_TOO_SMALL );
+ }
+
+ //
+ // Return the Connection name in the form \\server\share<NUL>
+ //
+
+ OutputBuffer->UncName[0] = L'\\';
+
+ RtlMoveMemory(
+ &OutputBuffer->UncName[1],
+ Path.Buffer,
+ Path.Length );
+
+ OutputBuffer->UncName[ (Path.Length + sizeof(WCHAR)) / sizeof(WCHAR) ] = L'\0';
+
+ Irp->IoStatus.Information = Path.Length + 2 * sizeof(WCHAR);
+
+try_exit: NOTHING;
+
+ } finally {
+ NOTHING;
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+DeleteConnection(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns removes a connection if force is specified or
+ if there are no open handles on this Vcb.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ PICB Icb;
+ PVCB Vcb;
+ PDCB Dcb;
+ PNONPAGED_DCB NonPagedDcb;
+ NODE_TYPE_CODE NodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "DeleteConnection...\n", 0);
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Wait to get to the head of the SCB queue. We do this in case
+ // we need to disconnect, so that we can send packets with the RCB
+ // resource held.
+ //
+
+ NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, &NonPagedDcb, &Icb );
+
+ if ( NodeTypeCode == NW_NTC_ICB_SCB ) {
+ IrpContext->pNpScb = Icb->SuperType.Scb->pNpScb;
+ } else {
+ ASSERT( NodeTypeCode == NW_NTC_ICB );
+ IrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb;
+ Dcb = NonPagedDcb->Fcb;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Acquire exclusive access to the RCB.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed, and that it is a directory handle.
+ //
+
+ if ( NodeTypeCode == NW_NTC_ICB_SCB ) {
+
+
+ if ( Icb->IsTreeHandle ) {
+
+ //
+ // Do an NDS logoff. This will release the RCB.
+ //
+
+ Status = NdsLogoff( IrpContext );
+ DebugTrace( 0, Dbg, "Nds tree logoff -> %08lx\n", Status );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Delete connection to SCB %X\n", Icb->SuperType.Scb );
+
+ Status = TreeDisconnectScb( IrpContext, Icb->SuperType.Scb );
+ DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status );
+
+ }
+
+ try_return( NOTHING );
+
+ } else if ( NodeTypeCode != NW_NTC_ICB ||
+ Dcb == NULL ||
+ ( Dcb->NodeTypeCode != NW_NTC_DCB &&
+ Dcb->NodeTypeCode != NW_NTC_FCB) ) {
+
+ DebugTrace(0, Dbg, "Invalid file handle\n", 0);
+
+ Status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ Vcb = Dcb->Vcb;
+ DebugTrace(0, Dbg, "Attempt to delete VCB = %08lx\n", Vcb);
+
+ //
+ // Vcb->OpenFileCount will be 1, (to account for this DCB), if the
+ // connection can be deleted.
+ //
+
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) ) {
+ DebugTrace(0, Dbg, "Cannot delete unredireced connection\n", 0);
+ try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
+ } else {
+
+ if ( Vcb->OpenFileCount > 1 ) {
+ DebugTrace(0, Dbg, "Cannot delete in use connection\n", 0);
+ Status = STATUS_CONNECTION_IN_USE;
+ } else {
+
+ //
+ // To delete the VCB, simply dereference it.
+ //
+
+ DebugTrace(0, Dbg, "Deleting connection\n", 0);
+
+ ClearFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ --Vcb->Scb->OpenFileCount;
+
+ NwDereferenceVcb( Vcb, IrpContext, TRUE );
+ }
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+
+ //
+ // An NDS logoff will have already freed the RCB
+ // and dequeued the irp context.
+ //
+
+ if ( ! ( Icb->IsTreeHandle ) ) {
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+EnumConnections(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the list of redirector connections.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP Context block for this request.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_SERVER_RESOURCE OutputBuffer;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PVCB Vcb;
+ PSCB Scb;
+ BOOLEAN OwnRcb;
+
+ UNICODE_STRING LocalName;
+ UNICODE_STRING ContainerName;
+ PCHAR FixedPortion;
+ PWCHAR EndOfVariableData;
+ ULONG EntrySize;
+ ULONG ResumeKey;
+
+ ULONG ShareType;
+ ULONG EntriesRead = 0;
+ ULONG EntriesRequested;
+ DWORD ConnectionType;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING Path;
+
+ DebugTrace(0, Dbg, "EnumConnections...\n", 0);
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.EnumConn.BytesNeeded ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer;
+ OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Acquire shared access to the drive map table.
+ //
+
+ NwAcquireSharedRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ //
+ // Initialize returned strings
+ //
+
+ RtlInitUnicodeString( &ContainerName, L"\\" );
+
+ FixedPortion = (PCHAR) OutputBuffer;
+ EndOfVariableData = (PWCHAR) ((ULONG) FixedPortion + OutputBufferLength);
+ ConnectionType = InputBuffer->Parameters.EnumConn.ConnectionType;
+
+ EntriesRequested = InputBuffer->Parameters.EnumConn.EntriesRequested;
+
+ //
+ // Run through the global VCB list looking for redirections.
+ //
+
+ ResumeKey = InputBuffer->Parameters.EnumConn.ResumeKey;
+
+ DebugTrace(0, Dbg, "Starting resume key is %d\n", InputBuffer->Parameters.EnumConn.ResumeKey );
+
+ for ( ListEntry = GlobalVcbList.Flink;
+ ListEntry != &GlobalVcbList &&
+ EntriesRequested > EntriesRead &&
+ Status == STATUS_SUCCESS ;
+ ListEntry = ListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry );
+
+ //
+ // Skip connections that we've already enumerated.
+ //
+
+ if ( Vcb->SequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) {
+ continue;
+ }
+
+ //
+ // Skip implicit connections, if they are not requested.
+ //
+
+ if ( !(ConnectionType & CONNTYPE_IMPLICIT) &&
+ !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) {
+
+ continue;
+ }
+
+ //
+ // Skip connections that are not requested.
+ //
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
+ if ( !( ConnectionType & CONNTYPE_PRINT ))
+ continue;
+ } else {
+ if ( !( ConnectionType & CONNTYPE_DISK ))
+ continue;
+ }
+
+
+ if ( Vcb->DriveLetter != 0 ) {
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
+ RtlInitUnicodeString( &LocalName, L"LPT1" );
+ LocalName.Buffer[3] = Vcb->DriveLetter;
+ ShareType = RESOURCETYPE_PRINT;
+ } else {
+ RtlInitUnicodeString( &LocalName, L"A:" );
+ LocalName.Buffer[0] = Vcb->DriveLetter;
+ ShareType = RESOURCETYPE_DISK;
+ }
+ } else { // No drive letter connection, i.e. UNC Connection
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ))
+ ShareType = RESOURCETYPE_PRINT;
+ else
+ ShareType = RESOURCETYPE_DISK;
+ }
+
+ if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
+ Path.Buffer = Vcb->Name.Buffer + 3;
+ Path.Length = Vcb->Name.Length - 6;
+ } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
+ Path.Buffer = Vcb->Name.Buffer + 5;
+ Path.Length = Vcb->Name.Length - 10;
+ } else {
+ Path = Vcb->Name;
+ }
+
+ // Strip off the unicode prefix
+
+ Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
+ Path.Length -= Vcb->Scb->UnicodeUid.Length;
+ Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
+
+ Status = WriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ &ContainerName,
+ Vcb->DriveLetter != 0 ? &LocalName : NULL,
+ &Path,
+ RESOURCE_CONNECTED,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ ShareType,
+ &EntrySize
+ );
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize;
+
+ } else if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+
+ EntriesRead++;
+ ResumeKey = Vcb->SequenceNumber;
+
+ DebugTrace(0, Dbg, "Returning VCB %08lx\n", Vcb );
+ DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey );
+ }
+ }
+
+ //
+ // Return the Servers we are connected to. This is most important for
+ // support of NetWare aware 16 bit apps.
+ //
+
+ if ((ConnectionType & CONNTYPE_IMPLICIT) &&
+ ( ConnectionType & CONNTYPE_DISK )) {
+
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNpScb;
+ PLIST_ENTRY NextScbQueueEntry;
+ ULONG EnumSequenceNumber = 0x80000000;
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ RtlInitUnicodeString( &ContainerName, L"\\\\" );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for ( ListEntry = ScbQueue.Flink;
+ ListEntry != &ScbQueue &&
+ EntriesRequested > EntriesRead &&
+ Status == STATUS_SUCCESS ;
+ ListEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks );
+ Scb = pNpScb->pScb;
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Skip connections that we've already enumerated.
+ //
+
+ if (( EnumSequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) ||
+
+ ( pNpScb == &NwPermanentNpScb ) ||
+
+ (( pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) &&
+ ( pNpScb->State != SCB_STATE_IN_USE ))) {
+
+ //
+ // Move to next entry in the list
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ EnumSequenceNumber++;
+ continue;
+ }
+
+ DebugTrace( 0, Dbg, " EnumConnections returning Servername = %wZ\n", &pNpScb->ServerName );
+
+ Status = WriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ &ContainerName,
+ NULL,
+ &pNpScb->ServerName,
+ RESOURCE_CONNECTED,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ RESOURCETYPE_DISK,
+ &EntrySize
+ );
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize;
+
+ } else if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+
+ EntriesRead++;
+ ResumeKey = EnumSequenceNumber;
+
+ DebugTrace(0, Dbg, "Returning SCB %08lx\n", Scb );
+ DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey );
+ }
+
+ //
+ // Move to next entry in the list
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ EnumSequenceNumber++;
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+ }
+
+ InputBuffer->Parameters.EnumConn.EntriesReturned = EntriesRead;
+ InputBuffer->Parameters.EnumConn.ResumeKey = ResumeKey;
+
+ if ( EntriesRead == 0 ) {
+
+ if (Status == STATUS_SUCCESS) {
+ Status = STATUS_NO_MORE_ENTRIES;
+ }
+
+ Irp->IoStatus.Information = 0;
+ }
+ else {
+ Irp->IoStatus.Information = OutputBufferLength;
+ }
+
+ } finally {
+ if (OwnRcb) {
+ NwReleaseRcb( &NwRcb );
+ }
+ }
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+WriteNetResourceEntry(
+ IN OUT PCHAR *FixedPortion,
+ IN OUT PWCHAR *EndOfVariableData,
+ IN PUNICODE_STRING ContainerName OPTIONAL,
+ IN PUNICODE_STRING LocalName OPTIONAL,
+ IN PUNICODE_STRING RemoteName,
+ IN ULONG ScopeFlag,
+ IN ULONG DisplayFlag,
+ IN ULONG UsageFlag,
+ IN ULONG ShareType,
+ OUT PULONG EntrySize
+ )
+/*++
+
+Routine Description:
+
+ This function packages a NETRESOURCE entry into the user output buffer.
+
+Arguments:
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a NETRESOURCE entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the
+ user information is written into the output buffer starting from
+ the end.
+
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ ContainerName - Supplies the full path qualifier to make RemoteName
+ a full UNC name.
+
+ LocalName - Supplies the local device name, if any.
+
+ RemoteName - Supplies the remote resource name.
+
+ ScopeFlag - Supplies the flag which indicates whether this is a
+ CONNECTED or GLOBALNET resource.
+
+ DisplayFlag - Supplies the flag which tells the UI how to display
+ the resource.
+
+ UsageFlag - Supplies the flag which indicates that the RemoteName
+ is either a container or a connectable resource or both.
+
+ ShareType - Type of the share connected to, RESOURCETYPE_PRINT or
+ RESOURCETYPE_DISK
+
+ EntrySize - Receives the size of the NETRESOURCE entry in bytes.
+
+Return Value:
+
+ STATUS_SUCCESS - Successfully wrote entry into user buffer.
+
+ STATUS_NO_MEMORY - Failed to allocate work buffer.
+
+ STATUS_MORE_ENTRIES - Buffer was too small to fit entry.
+
+--*/
+{
+ BOOLEAN FitInBuffer = TRUE;
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion;
+ UNICODE_STRING TmpRemote;
+
+ PAGED_CODE();
+
+ *EntrySize = sizeof(NETRESOURCEW) +
+ RemoteName->Length + NwProviderName.Length + 2 * sizeof(WCHAR);
+
+ if (ARGUMENT_PRESENT(LocalName)) {
+ *EntrySize += LocalName->Length + sizeof(WCHAR);
+ }
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ *EntrySize += ContainerName->Length;
+ }
+
+ //
+ // See if buffer is large enough to fit the entry.
+ //
+ if (((ULONG) *FixedPortion + *EntrySize) >
+ (ULONG) *EndOfVariableData) {
+
+ return STATUS_MORE_ENTRIES;
+ }
+
+ NetR->dwScope = ScopeFlag;
+ NetR->dwType = ShareType;
+ NetR->dwDisplayType = DisplayFlag;
+ NetR->dwUsage = UsageFlag;
+ NetR->lpComment = NULL;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (ULONG) (*FixedPortion) += sizeof(NETRESOURCEW);
+
+ //
+ // RemoteName
+ //
+ if (ARGUMENT_PRESENT(ContainerName)) {
+
+ //
+ // Prefix the RemoteName with its container name making the
+ // it a fully-qualified UNC name.
+ //
+
+ TmpRemote.MaximumLength = RemoteName->Length + ContainerName->Length + sizeof(WCHAR);
+ TmpRemote.Buffer = ALLOCATE_POOL(
+ PagedPool,
+ RemoteName->Length + ContainerName->Length + sizeof(WCHAR)
+ );
+
+ if (TmpRemote.Buffer == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlCopyUnicodeString(&TmpRemote, ContainerName);
+ RtlAppendUnicodeStringToString(&TmpRemote, RemoteName);
+ }
+ else {
+ TmpRemote = *RemoteName;
+ }
+
+ FitInBuffer = CopyStringToBuffer(
+ TmpRemote.Buffer,
+ TmpRemote.Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpRemoteName
+ );
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ FREE_POOL(TmpRemote.Buffer);
+ }
+
+ ASSERT(FitInBuffer);
+
+ //
+ // LocalName
+ //
+ if (ARGUMENT_PRESENT(LocalName)) {
+ FitInBuffer = CopyStringToBuffer(
+ LocalName->Buffer,
+ LocalName->Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpLocalName
+ );
+
+ ASSERT(FitInBuffer);
+ }
+ else {
+ NetR->lpLocalName = NULL;
+ }
+
+ //
+ // ProviderName
+ //
+
+ FitInBuffer = CopyStringToBuffer(
+ NwProviderName.Buffer,
+ NwProviderName.Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpProvider
+ );
+
+ ASSERT(FitInBuffer);
+
+ if (! FitInBuffer) {
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+BOOL
+CopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This is based on ..\nwlib\NwlibCopyStringToBuffer
+
+ This routine puts a single variable-length string into an output buffer.
+ The string is not written if it would overwrite the last fixed structure
+ in the buffer.
+
+Arguments:
+
+ SourceString - Supplies a pointer to the source string to copy into the
+ output buffer. If SourceString is null then a pointer to a zero terminator
+ is inserted into output buffer.
+
+ CharacterCount - Supplies the length of SourceString, not including zero
+ terminator. (This in units of characters - not bytes).
+
+ FixedDataEnd - Supplies a pointer to just after the end of the last
+ fixed structure in the buffer.
+
+ EndOfVariableData - Supplies an address to a pointer to just after the
+ last position in the output buffer that variable data can occupy.
+ Returns a pointer to the string written in the output buffer.
+
+ VariableDataPointer - Supplies a pointer to the place in the fixed
+ portion of the output buffer where a pointer to the variable data
+ should be written.
+
+Return Value:
+
+ Returns TRUE if string fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ DWORD CharsNeeded = (CharacterCount + 1);
+
+ PAGED_CODE();
+
+ //
+ // Determine if source string will fit, allowing for a zero terminator.
+ // If not, just set the pointer to NULL.
+ //
+
+ if ((*EndOfVariableData - CharsNeeded) >= FixedDataEnd) {
+
+ //
+ // It fits. Move EndOfVariableData pointer up to the location where
+ // we will write the string.
+ //
+
+ *EndOfVariableData -= CharsNeeded;
+
+ //
+ // Copy the string to the buffer if it is not null.
+ //
+
+ if (CharacterCount > 0 && SourceString != NULL) {
+
+ (VOID) wcsncpy(*EndOfVariableData, SourceString, CharacterCount);
+ }
+
+ //
+ // Set the zero terminator.
+ //
+
+ *(*EndOfVariableData + CharacterCount) = L'\0';
+
+ //
+ // Set up the pointer in the fixed data portion to point to where the
+ // string is written.
+ //
+
+ *VariableDataPointer = *EndOfVariableData;
+
+ return TRUE;
+
+ }
+ else {
+
+ //
+ // It doesn't fit. Set the offset to NULL.
+ //
+
+ *VariableDataPointer = NULL;
+
+ return FALSE;
+ }
+}
+
+
+NTSTATUS
+GetRemoteHandle(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the NetWare handle for a Directory. This is used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PCHAR OutputBuffer;
+ PICB Icb;
+ PDCB Dcb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetRemoteHandle\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ }
+
+ Dcb = (PDCB)Icb->SuperType.Fcb;
+ nodeTypeCode = Dcb->NodeTypeCode;
+
+ if ( nodeTypeCode != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+#if 1
+ if ( nodeTypeCode != NW_NTC_FCB ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Return the 6 byte NetWare handle for this file.
+ //
+
+ if (!Icb->HasRemoteHandle) {
+
+ Status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ if ( OutputBufferLength < sizeof( UCHAR ) ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ RtlCopyMemory( OutputBuffer, Icb->Handle, 6 * sizeof(CHAR));
+ IrpContext->pOriginalIrp->IoStatus.Information = 6 * sizeof(CHAR);
+
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+#else
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+#endif
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ if ( OutputBufferLength < sizeof( UCHAR ) ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if ( Icb->HasRemoteHandle ) {
+
+ // Already been asked for the handle
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ *OutputBuffer = Icb->Handle[0];
+
+ IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR);
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ CHAR Handle;
+
+ IrpContext->pScb = Dcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE,
+ Dcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Dcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Handle );
+
+ if (NT_SUCCESS(Status)) {
+ *OutputBuffer = Handle;
+ Icb->Handle[0] = Handle;
+ Icb->HasRemoteHandle = TRUE;
+ IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR);
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ DebugTrace( 0, Dbg, " -> %02x\n", Handle );
+
+ }
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetUserName(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the UserName that would be used to connect to a particular
+ server.
+
+ If there are credentials specific to this connection use them
+ otherwise use the logon credentials.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PWSTR InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PWSTR OutputBuffer;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING UidServer;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING ConvertedName;
+ PUNICODE_STRING pUserName;
+ PSCB pScb;
+ PLOGON pLogon;
+ BOOLEAN CredentialsHeld = FALSE;
+ BOOLEAN FailedTreeLookup = FALSE;
+ PNDS_SECURITY_CONTEXT pNdsCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetUserName\n", 0);
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ ServerName.Buffer = InputBuffer;
+ ServerName.MaximumLength = (USHORT)InputBufferLength;
+ ServerName.Length = (USHORT)InputBufferLength;
+ Status = MakeUidServer( &UidServer, &Uid, &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status );
+ return(Status);
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer );
+
+ //
+ // Get the login for this user.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE);
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // First try this name as a server. Avoid FindScb creating a
+ // connection to the server if one doesn't exist already.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT );
+ NwFindScb( &pScb, IrpContext, &UidServer, &ServerName );
+
+ pUserName = NULL;
+
+ //
+ // Look for bindery server name, or tree login name.
+ //
+
+ if ( pScb != NULL ) {
+
+ if ( pScb->UserName.Buffer != NULL ) {
+
+ pUserName = &pScb->UserName;
+
+ } else if ( pScb->NdsTreeName.Buffer != NULL &&
+ pScb->NdsTreeName.Length > 0 ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ CredentialsHeld = TRUE;
+
+ if ( pNdsCredentials->Credential ) {
+
+ //
+ // If we have login data, get the user name.
+ //
+
+ ConvertedName.Length = pNdsCredentials->Credential->userNameLength -
+ sizeof( WCHAR );
+ ConvertedName.MaximumLength = ConvertedName.Length;
+ ConvertedName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsCredentials->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsCredentials->Credential->optDataSize );
+
+ pUserName = &ConvertedName;
+
+ } else {
+
+ //
+ // If there's no credential data, we're not logged in.
+ //
+
+ FailedTreeLookup = TRUE;
+ }
+
+ } else {
+
+ FailedTreeLookup = TRUE;
+ }
+
+ }
+
+ }
+
+ //
+ // If it wasn't a server and we haven't already tried a tree, do so now.
+ //
+
+ if ( pUserName == NULL &&
+ !FailedTreeLookup ) {
+
+ Status = NdsLookupCredentials( &ServerName,
+ pLogon,
+ &pNdsCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ CredentialsHeld = TRUE;
+
+ if ( pNdsCredentials->Credential ) {
+
+ //
+ // If we've logged in, get the user name.
+ //
+
+ ConvertedName.Length = pNdsCredentials->Credential->userNameLength -
+ sizeof( WCHAR );
+ ConvertedName.MaximumLength = ConvertedName.Length;
+ ConvertedName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsCredentials->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsCredentials->Credential->optDataSize );
+
+ pUserName = &ConvertedName;
+
+ }
+ }
+
+ }
+
+ //
+ // If we still don't know, return the default name.
+ //
+
+ if ( pUserName == NULL &&
+ pLogon != NULL ) {
+
+ pUserName = &pLogon->UserName;
+ }
+
+ FREE_POOL(UidServer.Buffer);
+
+ if ( pUserName ) {
+
+ DebugTrace( 0, Dbg, "Get User Name: %wZ\n", pUserName );
+
+ try {
+
+ if (pUserName->Length > OutputBufferLength) {
+
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ReleaseAndExit;
+ }
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ IrpContext->pOriginalIrp->IoStatus.Information = pUserName->Length;
+ RtlMoveMemory( OutputBuffer, pUserName->Buffer, pUserName->Length);
+
+ Status = STATUS_SUCCESS;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ReleaseAndExit:
+
+ if ( pScb ) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status );
+
+ if ( CredentialsHeld ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+GetChallenge(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds the challenge and session key for rpc using the
+ credentials stored in the redirector. The Rpc client can supply a
+ password. This allows the redirector to keep the algorithm in one
+ place.
+
+ If a password is supplied then use that, if there is a password on this
+ specific connection use that, otherwise use the logon credentials.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_GET_CHALLENGE_REQUEST InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_CHALLENGE_REPLY OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ OEM_STRING Password;
+ PSCB pScb;
+ PLOGON pLogon;
+ BOOLEAN RcbHeld = FALSE;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER ProcessUid;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetChallenge\n", 0);
+
+ if ((InputBufferLength <
+ (FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])) +
+ InputBuffer->ServerNameorPasswordLength)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Only allow processes running in the system context to call this api to prevent
+ // password attacks.
+ //
+ SeCaptureSubjectContext(&SubjectContext);
+ SeQueryAuthenticationIdToken(&SubjectContext.PrimaryToken, (PLUID)&ProcessUid);
+ SeReleaseSubjectContext(&SubjectContext);
+
+ // FIXFIX surely there's a define for 3e7 somewhere.
+
+ if (ProcessUid.QuadPart != 0x3e7) {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Password.Buffer = NULL;
+
+ if ( InputBuffer->Flags == CHALLENGE_FLAGS_SERVERNAME ) {
+
+ PUNICODE_STRING pPassword;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING UidServer;
+
+ if (InputBuffer->ServerNameorPasswordLength == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We have to supply the password from the redirector
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ ServerName.Buffer = (PWSTR)((PUCHAR)InputBuffer +
+ FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]));
+ ServerName.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength;
+ ServerName.Length = (USHORT)InputBuffer->ServerNameorPasswordLength;
+
+ Status = MakeUidServer( &UidServer, &Uid, &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return(Status);
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer );
+
+ //
+ // Avoid FindScb creating a connection to the server if one
+ // doesn't exist already.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT );
+ NwFindScb( &pScb, IrpContext, &UidServer, &ServerName );
+
+ try {
+
+ if ((pScb != NULL) &&
+ (pScb->Password.Buffer != NULL)) {
+
+ pPassword = &pScb->Password;
+
+ } else {
+
+ //
+ // Use default credentials for this UID
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ RcbHeld = TRUE;
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE);
+
+ if (pLogon != NULL ) {
+
+ pPassword = &pLogon->PassWord;
+
+ } else {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", STATUS_ACCESS_DENIED );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+ if (pPassword->Length != 0) {
+ Status = RtlUpcaseUnicodeStringToOemString( &Password, pPassword, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return( Status );
+ }
+ } else {
+ Password.Buffer = "";
+ Password.Length = Password.MaximumLength = 0;
+ }
+
+ } finally {
+
+ if (RcbHeld) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if (pScb != NULL) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ FREE_POOL(UidServer.Buffer);
+ }
+
+ } else {
+
+ UNICODE_STRING LocalPassword;
+
+ LocalPassword.Buffer = (PWSTR)((PUCHAR)InputBuffer +
+ FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]));
+ LocalPassword.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength;
+ LocalPassword.Length = (USHORT)InputBuffer->ServerNameorPasswordLength;
+
+ if (LocalPassword.Length != 0) {
+ Status = RtlUpcaseUnicodeStringToOemString( &Password, &LocalPassword, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return( Status );
+ }
+ } else {
+ Password.Buffer = "";
+ Password.Length = Password.MaximumLength = 0;
+ }
+ }
+
+ DebugTrace( 0, Dbg, " ->Password = \"%Z\"\n", &Password );
+
+ try {
+ RespondToChallenge( (PUCHAR)&InputBuffer->ObjectId, &Password, InputBuffer->Challenge, OutputBuffer->Challenge);
+
+ } finally {
+
+ if ( Password.Length > 0 ) {
+
+ RtlFreeAnsiString( &Password );
+ }
+ }
+
+ Irp->IoStatus.Information = sizeof(NWR_GET_CHALLENGE_REPLY);
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return Status;
+}
+
+NTSTATUS
+WriteConnStatusEntry(
+ PSCB pConnectionScb,
+ PBYTE pbUserBuffer,
+ DWORD dwBufferLen,
+ DWORD *pdwBytesWritten,
+ DWORD *pdwBytesNeeded,
+ BOOLEAN fCallerScb
+ )
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ BOOLEAN fHoldingCredentials = FALSE;
+ PUNICODE_STRING puUserName = NULL;
+ UNICODE_STRING CredentialName;
+ UNICODE_STRING ServerName;
+ PCONN_STATUS pStatus;
+ DWORD dwBytesNeeded;
+ PBYTE pbStrPtr;
+ DWORD dwAllowedHandles;
+
+ //
+ // If this is an NDS connection, get the credentials.
+ //
+
+ if ( ( pConnectionScb->MajorVersion > 3 ) &&
+ ( pConnectionScb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pConnectionScb->UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &(pConnectionScb->NdsTreeName),
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ fHoldingCredentials = TRUE;
+
+ if ( pNdsContext->Credential != NULL ) {
+
+ CredentialName.Length = pNdsContext->Credential->userNameLength -
+ sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ puUserName = &CredentialName;
+ }
+
+ }
+ }
+
+ } else {
+
+ if ( pConnectionScb->UserName.Length != 0 ) {
+ puUserName = &(pConnectionScb->UserName);
+ } else {
+ puUserName = NULL;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "WriteConnStatus: UserName %wZ\n", puUserName );
+
+ //
+ // Strip off the uid from the server name.
+ //
+
+ ServerName.Length = (pConnectionScb->UidServerName).Length;
+ ServerName.Buffer = (pConnectionScb->UidServerName).Buffer;
+
+ while ( ServerName.Length ) {
+
+ if ( ServerName.Buffer[0] == L'\\' ) {
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+ break;
+ }
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+
+ }
+
+ DebugTrace( 0, Dbg, "WriteConnStatus: ServerName %wZ\n", &ServerName );
+
+ //
+ // Do we have enough space? Don't forget that we have to
+ // NULL terminate the WCHAR strings.
+ //
+
+ dwBytesNeeded = sizeof( CONN_STATUS );
+
+ dwBytesNeeded += ( ServerName.Length + sizeof( WCHAR ) );
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+ dwBytesNeeded += ( pConnectionScb->NdsTreeName.Length + sizeof( WCHAR ) );
+ }
+
+ if ( puUserName ) {
+ dwBytesNeeded += ( puUserName->Length + sizeof( WCHAR ) );
+ }
+
+ //
+ // Pad the end to make sure all structures are aligned.
+ //
+
+ dwBytesNeeded = ROUNDUP4( dwBytesNeeded );
+
+ if ( dwBytesNeeded > dwBufferLen ) {
+
+ *pdwBytesNeeded = dwBytesNeeded;
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Fill in the CONN_STATUS structure.
+ //
+
+ try {
+
+ pStatus = (PCONN_STATUS)pbUserBuffer;
+ pbStrPtr = pbUserBuffer + sizeof( CONN_STATUS );
+
+ //
+ // We always have a server name.
+ //
+
+ pStatus->pszServerName = (PWSTR) pbStrPtr;
+ pbStrPtr += ( ServerName.Length + sizeof( WCHAR ) );
+
+ //
+ // Fill in the user name if applicable.
+ //
+
+ if ( puUserName ) {
+
+ pStatus->pszUserName = (PWSTR) pbStrPtr;
+ pbStrPtr += ( puUserName->Length + sizeof( WCHAR ) );
+
+ } else {
+
+ pStatus->pszUserName = NULL;
+ }
+
+ //
+ // Fill in the tree name if applicable.
+ //
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+
+ pStatus->pszTreeName = (PWSTR) pbStrPtr;
+
+ } else {
+
+ pStatus->pszTreeName = NULL;
+ }
+
+ //
+ // Fill in the connection number if applicable.
+ //
+
+ if ( ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) ||
+ ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ) {
+
+ pStatus->nConnNum = (DWORD)(pConnectionScb->pNpScb->ConnectionNo);
+
+ } else {
+
+ pStatus->nConnNum = 0;
+
+ }
+
+ //
+ // Copy the user name over.
+ //
+
+ if ( puUserName ) {
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszUserName),
+ (PBYTE)(puUserName->Buffer),
+ puUserName->Length );
+ *(pStatus->pszUserName + (puUserName->Length / sizeof( WCHAR ))) = L'\0';
+
+ }
+
+ //
+ // Set the NDS flag and authentication fields.
+ //
+
+ if ( ( pConnectionScb->MajorVersion > 3 ) &&
+ ( pConnectionScb->UserName.Length == 0 ) ) {
+
+ pStatus->fNds = TRUE;
+
+ if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ if ( ( pConnectionScb->VcbCount ) || ( pConnectionScb->OpenNdsStreams ) ) {
+ pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_LICENSED;
+ } else {
+ pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_NO_LICENSE;
+ }
+
+ } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+
+ pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED;
+
+ } else {
+
+ pStatus->dwConnType = NW_CONN_DISCONNECTED;
+
+ }
+
+ } else {
+
+ pStatus->fNds = FALSE;
+
+ if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ pStatus->dwConnType = NW_CONN_BINDERY_LOGIN;
+
+ } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+
+ pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED;
+
+ } else {
+
+ pStatus->dwConnType = NW_CONN_DISCONNECTED;
+
+ }
+
+ }
+
+ //
+ // Copy over the tree name.
+ //
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszTreeName),
+ (PBYTE)(pConnectionScb->NdsTreeName.Buffer),
+ pConnectionScb->NdsTreeName.Length );
+ *( pStatus->pszTreeName +
+ ( pConnectionScb->NdsTreeName.Length / sizeof( WCHAR ) ) ) = L'\0';
+
+ } else {
+
+ pStatus->pszTreeName = NULL;
+ }
+
+ //
+ // Copy the server name over.
+ //
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszServerName),
+ (PBYTE)(ServerName.Buffer),
+ ServerName.Length );
+ *(pStatus->pszServerName + (ServerName.Length / sizeof( WCHAR ))) = L'\0';
+
+ //
+ // Set the preferred server field if this is a preferred server
+ // and there are no explicit uses for the connection. If the
+ // fCallerScb parameter is TRUE, then this SCB has a handle from
+ // the caller of the API and we have to make an allowance for
+ // that handle. Yes, this is kind of ugly.
+ //
+
+ if ( fCallerScb ) {
+ dwAllowedHandles = 1;
+ } else {
+ dwAllowedHandles = 0;
+ }
+
+ if ( ( pConnectionScb->PreferredServer ) &&
+ ( pConnectionScb->OpenFileCount == 0 ) &&
+ ( pConnectionScb->IcbCount == dwAllowedHandles ) ) {
+
+ pStatus->fPreferred = TRUE;
+
+ } else {
+
+ pStatus->fPreferred = FALSE;
+ }
+
+ //
+ // Fill out the length.
+ //
+
+ pStatus->dwTotalLength = dwBytesNeeded;
+ *pdwBytesWritten = dwBytesNeeded;
+ Status = STATUS_SUCCESS;
+
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ DebugTrace( 0, Dbg, "Exception %08lx accessing user mode buffer.\n", Status );
+ goto ExitWithCleanup;
+
+ }
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentials ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetConnStatus(
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_OBJECT FileObject
+ )
+/*++
+
+ Get the connection status for the described connection.
+ The following connection requests are valid:
+
+ Server (e.g. "MARS312") - returns a single connection
+ status structure for this server if the user has a
+ connection to the server.
+
+ Tree (e.g. "*MARSDEV") - returns a connection status
+ structure for every server in the tree that the user
+ has a connection to.
+
+ All Connections (e.g. "") - returns a connection status
+ structure for every server that the user has a
+ connection to.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ BYTE *OutputBuffer;
+ ULONG OutputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING ConnectionName, UidServer;
+ BOOL fTreeConnections = FALSE;
+ BOOL fServerConnection = FALSE;
+ BOOL OwnRcb = FALSE;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ DWORD dwBytesWritten, dwBytesNeeded;
+ KIRQL OldIrql;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ DWORD dwReturned = 0;
+ ULONG SequenceNumber = 0;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB pIcb;
+ PSCB pCallerScb;
+ PVOID fsContext, fsContext2;
+
+ //
+ // Get the appropriate buffers.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ //
+ // Figure out who this request applies to.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ RtlInitUnicodeString( &ConnectionName, NULL );
+ RtlInitUnicodeString( &UidServer, NULL );
+
+ //
+ // Figure out who the caller of this routine is so we know to
+ // ignore their handle when deciding what to return.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 );
+
+ if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ pIcb = (PICB) fsContext2;
+ pCallerScb = pIcb->SuperType.Scb;
+ DebugTrace( 0, Dbg, "GetConnStatus called by handle on %08lx\n", pCallerScb );
+
+ } else {
+
+ pCallerScb = NULL;
+ DebugTrace( 0, Dbg, "Couldn't figure out who called us.\n", 0 );
+ }
+
+ //
+ //
+ // Figure out which connections we're looking for.
+ //
+
+ try {
+
+ if ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength != 0 ) {
+
+ if ( InputBuffer->Parameters.GetConnStatus.ConnectionName[0] == L'*' ) {
+
+ ConnectionName.Buffer = &(InputBuffer->Parameters.GetConnStatus.ConnectionName[1]);
+ ConnectionName.Length = (USHORT)
+ ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength -
+ sizeof( WCHAR ) );
+ ConnectionName.MaximumLength = ConnectionName.Length;
+
+ fTreeConnections = TRUE;
+
+ DebugTrace( 0, Dbg, "GetConnStatus: Tree is %wZ\n", &ConnectionName );
+
+ } else {
+
+ ConnectionName.Buffer = InputBuffer->Parameters.GetConnStatus.ConnectionName;
+ ConnectionName.Length = (USHORT)
+ (InputBuffer->Parameters.GetConnStatus.ConnectionNameLength);
+ ConnectionName.MaximumLength = ConnectionName.Length;
+
+ fServerConnection = TRUE;
+
+ Status = MakeUidServer( &UidServer, &Uid, &ConnectionName );
+ if ( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnStatus: Server is %wZ\n", &UidServer );
+ }
+
+ } else {
+
+ DebugTrace( 0, Dbg, "GetConnectionStatus: Enumerate all connections.\n", 0 );
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad input buffer in GetConnStatus.\n" , 0 );
+
+ }
+
+ //
+ // If this is a server connection, find and return it.
+ //
+
+ if ( fServerConnection ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServer, 0 );
+
+ if ( !PrefixEntry ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+
+ if ( ( pScb->PreferredServer ) ||
+ ( pScb->OpenFileCount > 0 ) ) {
+
+ //
+ // If there are open files, we need to return this.
+ // We always write status entries for the preferred
+ // server so that we can give default logon info.
+ //
+
+ goto ProcessServer;
+ }
+
+ //
+ // Are there open handles other than the caller?
+ //
+
+ if ( pScb == pCallerScb ) {
+
+ if ( pScb->IcbCount > 1 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 1 );
+ goto ProcessServer;
+ }
+
+ } else {
+
+ if ( pScb->IcbCount > 0 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 0 );
+ goto ProcessServer;
+ }
+ }
+
+ //
+ // Not an explicit use for this server.
+ //
+ goto ExitWithCleanup;
+
+ProcessServer:
+
+ NwReferenceScb( pScb->pNpScb );
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ Status = WriteConnStatusEntry( pScb,
+ OutputBuffer,
+ OutputBufferLength,
+ &dwBytesWritten,
+ &dwBytesNeeded,
+ (BOOLEAN)( pScb == pCallerScb ) );
+
+ NwDereferenceScb( pScb->pNpScb );
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = 0;
+
+ if ( !NT_SUCCESS( Status )) {
+
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = 0;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = dwBytesNeeded;
+ Irp->IoStatus.Information = 0;
+ goto ExitWithCleanup;
+
+ } else {
+
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = 1;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0;
+ Irp->IoStatus.Information = dwBytesWritten;
+ goto ExitWithCleanup;
+
+ }
+ }
+
+ //
+ // We want all connections or all tree connections, so
+ // we need to walk the list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = ScbQueue.Flink;
+
+ while ( ListEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks );
+ pScb = pNpScb->pScb;
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Make sure we pass up the one's we've already returned.
+ //
+
+ if ( ( SequenceNumber >= InputBuffer->Parameters.GetConnStatus.ResumeKey ) &&
+ ( pNpScb != &NwPermanentNpScb ) ) {
+
+ //
+ // If there are open files, we need to return this.
+ // We always write status entries for the preferred
+ // server so that we can give default logon info.
+ //
+
+ if ( ( pScb->PreferredServer ) ||
+ ( pScb->OpenFileCount > 0 ) ) {
+ goto SecondProcessServer;
+ }
+
+ //
+ // Are there any handles other than the caller?
+ //
+
+ if ( pScb == pCallerScb ) {
+
+ if ( pScb->IcbCount > 1 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 2 );
+ goto SecondProcessServer;
+ }
+
+ } else {
+
+ if ( pScb->IcbCount > 0 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 1 );
+ goto SecondProcessServer;
+ }
+ }
+
+ }
+
+ //
+ // Not an interesting server; move to next entry.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ continue;
+
+SecondProcessServer:
+
+ //
+ // We have a possible candidate; see if the uid and tree are appropriate.
+ //
+
+ if ( ( (pScb->UserUid).QuadPart != Uid.QuadPart ) ||
+
+ ( fTreeConnections &&
+ !RtlEqualUnicodeString( &(pScb->NdsTreeName),
+ &ConnectionName,
+ TRUE ) ) ) {
+
+ //
+ // No dice. Move onto the next one.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ continue;
+
+ }
+
+ //
+ // Ok, we definitely want to report this one.
+ //
+
+ Status = WriteConnStatusEntry( pScb,
+ OutputBuffer,
+ OutputBufferLength,
+ &dwBytesWritten,
+ &dwBytesNeeded,
+ (BOOLEAN)( pScb == pCallerScb ) );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // If we couldn't write this entry, then we have to update
+ // the ResumeKey and return. We don't really know how many
+ // more there are going to be so we 'suggest' to the caller
+ // a 2k buffer size.
+ //
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = SequenceNumber;
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 2048;
+ NwDereferenceScb( pNpScb );
+ goto ExitWithCleanup;
+
+ } else {
+
+ OutputBuffer = ( OutputBuffer + dwBytesWritten );
+ OutputBufferLength -= dwBytesWritten;
+ dwReturned++;
+ }
+
+ //
+ // Move to next entry in the list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ }
+
+ //
+ // We made it through the list.
+ //
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = 0;
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0;
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ //
+ // If we returned any entries, then set the status to success.
+ //
+
+ if ( dwReturned ) {
+
+ ASSERT( SequenceNumber != 0 );
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( UidServer.Buffer != NULL ) {
+ FREE_POOL( UidServer.Buffer );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetConnectionInfo(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetConnectionInfo:
+
+ Takes a connection name from the new shell and returns
+ some info commonly requested by property sheets and the
+ such.
+
+ The following connection names are supported:
+
+ Drive Letter: "X:"
+ Printer Port: "LPTX:"
+ UNC Name: "\\SERVER\Share\{Path\}
+
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer;
+ PCONN_INFORMATION pConnInfo;
+ ULONG InputBufferLength, OutputBufferLength;
+ ULONG BytesNeeded;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING ConnectionName;
+ UNICODE_STRING UidVolumeName;
+ WCHAR DriveLetter = 0;
+
+ BOOLEAN OwnRcb = FALSE;
+ BOOLEAN ReferenceVcb = FALSE;
+ PVCB Vcb = NULL;
+ PSCB Scb = NULL;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+
+ PLOGON pLogon;
+ UNICODE_STRING CredentialName;
+ UNICODE_STRING ServerName;
+ PUNICODE_STRING puUserName = NULL;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ BOOLEAN fHoldingCredentials = FALSE;
+
+ //
+ // Get the input and output buffers.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&pConnInfo );
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ RtlInitUnicodeString( &UidVolumeName, NULL );
+
+ ConnectionName.Length = (USHORT)(InputBuffer->Parameters).GetConnInfo.ConnectionNameLength;
+ ConnectionName.MaximumLength = ConnectionName.Length;
+ ConnectionName.Buffer = &((InputBuffer->Parameters).GetConnInfo.ConnectionName[0]);
+
+ //
+ // Ok, this gets a little hand-wavey, but we have to try and figure
+ // what this connection name represents.
+ //
+
+ if ( ConnectionName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = ConnectionName.Buffer[0];
+ } else if ( ConnectionName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = ConnectionName.Buffer[3];
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ if ( DriveLetter != 0 ) {
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: Drive %wZ\n", &ConnectionName );
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) ||
+ ( DriveLetter >= L'1' && DriveLetter <= L'9' ) );
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ( ( Vcb != NULL ) && ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ } else {
+
+ //
+ // This is a UNC path. Skip over the backslashes and
+ // prepend the unicode uid.
+ //
+
+ ConnectionName.Length -= (2 * sizeof( WCHAR ) );
+ ConnectionName.Buffer += 2;
+
+ Status = MakeUidServer( &UidVolumeName, &Uid, &ConnectionName );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: %wZ\n", &UidVolumeName );
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 );
+
+ if ( Prefix != NULL ) {
+ Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry );
+
+ if ( Vcb->Name.Length != UidVolumeName.Length ) {
+ Vcb = NULL;
+ }
+ }
+ }
+
+ if ( !Vcb ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: Vcb is 0x%08lx\n", Vcb );
+
+ NwReferenceVcb( Vcb );
+ ReferenceVcb = TRUE;
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ //
+ // Get the username. This is the same code block as in
+ // WriteConnStatusEntry; it should be abstracted out.
+ //
+
+ Scb = Vcb->Scb;
+ ASSERT( Scb != NULL );
+
+ if ( ( Scb->MajorVersion > 3 ) &&
+ ( Scb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &(Scb->NdsTreeName),
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ fHoldingCredentials = TRUE;
+
+ if ( pNdsContext->Credential != NULL ) {
+
+ CredentialName.Length = pNdsContext->Credential->userNameLength -
+ sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ puUserName = &CredentialName;
+ }
+
+ }
+ }
+
+ } else {
+
+ puUserName = &(Scb->UserName);
+
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: UserName %wZ\n", puUserName );
+
+ //
+ // Strip off the uid from the server name.
+ //
+
+ ServerName.Length = (Scb->UidServerName).Length;
+ ServerName.Buffer = (Scb->UidServerName).Buffer;
+
+ while ( ServerName.Length ) {
+
+ if ( ServerName.Buffer[0] == L'\\' ) {
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+ break;
+ }
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: ServerName %wZ\n", &ServerName );
+
+ //
+ // Write a single CONN_INFORMATION structure into the output buffer.
+ //
+
+ if ( puUserName ) {
+
+ BytesNeeded = sizeof( CONN_INFORMATION ) +
+ ServerName.Length +
+ puUserName->Length;
+ } else {
+
+ BytesNeeded = sizeof( CONN_INFORMATION ) +
+ ServerName.Length;
+
+ }
+
+ if ( BytesNeeded > OutputBufferLength ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ pConnInfo->HostServerLength = ServerName.Length;
+ pConnInfo->HostServer = (LPWSTR) ( (PBYTE) pConnInfo ) + sizeof( CONN_INFORMATION );
+ RtlCopyMemory( pConnInfo->HostServer, ServerName.Buffer, ServerName.Length );
+
+ pConnInfo->UserName = (LPWSTR) ( ( (PBYTE) pConnInfo->HostServer ) +
+ ServerName.Length );
+
+ if ( puUserName ) {
+
+ pConnInfo->UserNameLength = puUserName->Length;
+ RtlCopyMemory( pConnInfo->UserName, puUserName->Buffer, puUserName->Length );
+
+ } else {
+
+ pConnInfo->UserNameLength = 0;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentials ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( ReferenceVcb ) {
+ NwDereferenceVcb( Vcb, NULL, FALSE );
+ }
+
+ if ( UidVolumeName.Buffer ) {
+ FREE_POOL( UidVolumeName.Buffer );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetPreferredServer(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetPreferredServer:
+
+ Returns the current preferred server.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ BYTE *OutputBuffer;
+ ULONG OutputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ PLOGON pLogon;
+
+ PUNICODE_STRING PreferredServer;
+
+ //
+ // Get the output buffer.
+ //
+
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Get the logon structure for the user and return the preferred server.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+
+ if ( ( pLogon ) &&
+ ( pLogon->ServerName.Length ) &&
+ ( ( pLogon->ServerName.Length + sizeof( UNICODE_STRING ) ) <= OutputBufferLength ) ) {
+
+ PreferredServer = (PUNICODE_STRING) OutputBuffer;
+ PreferredServer->Length = pLogon->ServerName.Length;
+ PreferredServer->MaximumLength = pLogon->ServerName.Length;
+ PreferredServer->Buffer = ( PWCHAR ) ( OutputBuffer + sizeof( UNICODE_STRING ) );
+
+ RtlCopyMemory( PreferredServer->Buffer,
+ pLogon->ServerName.Buffer,
+ pLogon->ServerName.Length );
+
+ Status = STATUS_SUCCESS;
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ return Status;
+}
+
+NTSTATUS
+GetConnectionPerformance(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetConnectionPerformance:
+
+ Takes a connection name from the new shell and returns
+ some estimated performance info to the shell so the shell
+ can decide whether or not it wants to download icons, etc.
+
+ The following connection names are supported:
+
+ Drive Letter: "X:"
+ Printer Port: "LPTX:"
+ UNC Name: "\\SERVER\Share\{Path\}
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING RemoteName;
+
+ WCHAR DriveLetter = 0;
+ BOOLEAN OwnRcb = FALSE;
+ BOOLEAN ReferenceScb = FALSE;
+ PVCB Vcb = NULL;
+ PSCB Scb = NULL;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING OriginalUnc;
+
+ //
+ // Get the input buffer.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ //
+ // Get the UID for the caller.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ //
+ // Dig out the remote name.
+ //
+
+ RemoteName.Length = (USHORT)(InputBuffer->Parameters).GetConnPerformance.RemoteNameLength;
+ RemoteName.MaximumLength = RemoteName.Length;
+ RemoteName.Buffer = &((InputBuffer->Parameters).GetConnPerformance.RemoteName[0]);
+
+ //
+ // Ok, this gets a little hand-wavey, but we have to try and figure
+ // what this connection name represents (just like in GetConnectionInfo).
+ //
+
+ if ( RemoteName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = RemoteName.Buffer[0];
+ } else if ( RemoteName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = RemoteName.Buffer[3];
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ DebugTrace( 0, Dbg, "GetConnectionPerformance: Remote Name %wZ\n", &RemoteName );
+
+ if ( DriveLetter != 0 ) {
+
+ if ( ! ( ( ( DriveLetter >= L'a' ) && ( DriveLetter <= L'z' ) ) ||
+ ( ( DriveLetter >= L'A' ) && ( DriveLetter <= L'Z' ) ) ||
+ ( ( DriveLetter >= L'0' ) && ( DriveLetter <= L'9' ) ) ) ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ if ( DriveLetter >= L'a' && DriveLetter <= L'z' ) {
+ DriveLetter += (WCHAR) ( L'A' - L'a' );
+ }
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Did we get a connection?
+ //
+
+ if ( Vcb == NULL ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ Scb = Vcb->Scb;
+
+ } else {
+
+ //
+ // It's valid for the shell to pass us the remote name of a drive
+ // with no reference to the drive at all. Since we file these in
+ // volume prefix table with their drive letter information, we won't
+ // find them if we do a flat munge and lookup. Therefore, we have
+ // to walk the global vcb list and find the match.
+ //
+
+ //
+ // Skip over the first slash of the provided UNC remote name.
+ //
+
+ RemoteName.Length -= sizeof( WCHAR );
+ RemoteName.Buffer += 1;
+
+ for ( ListEntry = GlobalVcbList.Flink;
+ ( ListEntry != &GlobalVcbList ) && ( Scb == NULL );
+ ListEntry = ListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry );
+
+ OriginalUnc.Length = Vcb->Name.Length;
+ OriginalUnc.MaximumLength = Vcb->Name.MaximumLength;
+ OriginalUnc.Buffer = Vcb->Name.Buffer;
+
+ if ( Vcb->DriveLetter ) {
+
+ //
+ // Try it as a drive connection.
+ //
+
+ while ( ( OriginalUnc.Length ) &&
+ ( OriginalUnc.Buffer[0] != L':' ) ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+ }
+
+ if ( OriginalUnc.Buffer[0] == L':' ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+
+ if ( RtlEqualUnicodeString( &OriginalUnc,
+ &RemoteName,
+ TRUE ) ) {
+ Scb = Vcb->Scb;
+ }
+ }
+
+ } else {
+
+ //
+ // Try it as a UNC connection; start by skipping
+ // only the leading slash, the walking to the next
+ // slash.
+ //
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+
+ while ( ( OriginalUnc.Length ) &&
+ ( OriginalUnc.Buffer[0] != L'\\' ) ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+ }
+
+ if ( OriginalUnc.Length ) {
+
+ if ( RtlEqualUnicodeString( &OriginalUnc,
+ &RemoteName,
+ TRUE ) ) {
+ Scb = Vcb->Scb;
+ }
+ }
+
+ }
+ }
+
+ }
+
+ if ( !Scb ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ NwReferenceScb( Scb->pNpScb );
+ ReferenceScb = TRUE;
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ DebugTrace( 0, Dbg, "GetConnectionPerformance: Scb is 0x%08lx\n", Scb );
+
+ //
+ // Now dig out the performance info from the LIP negotiation.
+ //
+ // dwSpeed - The speed of the media to the network resource in units of 100bps (e.g 1,200
+ // baud point to point link returns 12).
+ // dwDelay - The delay introduced by the network when sending information (i.e. the time
+ // between starting sending data and the time that it starts being received) in
+ // units of a millisecond. This is in addition to any latency that was incorporated
+ // into the calculation of dwSpeed, so the value returned will be 0 for accessing
+ // most resources.
+ // dwOptDataSize - A recommendation for the size of data in bytes that is most efficiently
+ // sent through the network when an application makes a single request to
+ // the network resource. For example, for a disk network resource, this
+ // value might be 2048 or 512 when writing a block of data.
+
+ (InputBuffer->Parameters).GetConnPerformance.dwFlags = WNCON_DYNAMIC;
+ (InputBuffer->Parameters).GetConnPerformance.dwDelay = 0;
+ (InputBuffer->Parameters).GetConnPerformance.dwOptDataSize = Scb->pNpScb->BufferSize;
+ (InputBuffer->Parameters).GetConnPerformance.dwSpeed = Scb->pNpScb->LipDataSpeed;
+
+ //
+ // BUGBUG: We don't return any good speed info for servers that have not yet
+ // negotiated lip. We may return out of date information for servers that have
+ // become disconnected unless a RAS line transition occurred. This API is bogus.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( ReferenceScb ) {
+ NwDereferenceScb( Scb->pNpScb );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+SetShareBit(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ )
+/*+++
+
+SetShareBit:
+
+ This function sets the share bit on a file.
+ The bit won't get set until all handles to the
+ file are closed.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB pIcb;
+ PFCB pFcb;
+ PVOID fsContext, fsContext2;
+
+ DebugTrace( 0, Dbg, "SetShareBit.\n", 0 );
+
+ //
+ // Make sure this is a handle to a file.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 );
+
+ if ( nodeTypeCode != NW_NTC_ICB ) {
+ DebugTrace( 0, Dbg, "You can only set the share bit on a file!\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pIcb = (PICB) fsContext2;
+ pFcb = pIcb->SuperType.Fcb;
+
+ if ( pFcb->NodeTypeCode != NW_NTC_FCB ) {
+ DebugTrace( 0, Dbg, "You can't set the share bit on a directory!\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Acquire this FCB so we can muck with the flags.
+ //
+
+ NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE );
+
+ SetFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE );
+
+ NwReleaseFcb( pFcb->NonPagedFcb );
+
+ return STATUS_SUCCESS;
+
+}
+
+VOID
+LazySetShareable(
+ PIRP_CONTEXT IrpContext,
+ PICB pIcb,
+ PFCB pFcb
+)
+/***
+
+Function Description:
+
+ This function gets called everytime an ICB with a remote handle
+ is closed. If we are closing the last ICB to an FCB and the
+ caller has requested that we set the shareable bit on the FCB,
+ then we need to do so now. Otherwise, we simply return.
+
+Caveats:
+
+ If we fail to set the shareable bit, there is no way to notify
+ the requestor of the operation that the operation was not carried
+ out.
+
+***/
+{
+
+ NTSTATUS Status;
+
+ PLIST_ENTRY IcbListEntry;
+ PICB pCurrentIcb;
+ BOOLEAN OtherHandlesExist = FALSE;
+
+ ULONG Attributes;
+ BOOLEAN AttributesAreValid = FALSE;
+
+
+ //
+ // Get to the head of the queue, acquire the RCB,
+ // and acquire this FCB to protect the ICB list
+ // and FCB flags.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE );
+
+ //
+ // Scan the other ICBs on this FCB to see if any of
+ // them have remote handles.
+ //
+
+ for ( IcbListEntry = pFcb->IcbList.Flink;
+ IcbListEntry != &(pFcb->IcbList) ;
+ IcbListEntry = IcbListEntry->Flink ) {
+
+ pCurrentIcb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry );
+
+ if ( ( pCurrentIcb != pIcb ) &&
+ ( pCurrentIcb->HasRemoteHandle ) ) {
+ OtherHandlesExist = TRUE;
+ }
+ }
+
+ if ( OtherHandlesExist ) {
+
+ //
+ // We'll do it when the last handle is closed.
+ //
+
+ DebugTrace( 0, Dbg, "LazySetShareable: This isn't the last remote handle.\n", 0 );
+ goto ReleaseAllAndExit;
+ }
+
+ //
+ // We're closing the last handle. Make sure we have valid attributes.
+ //
+
+ if ( !FlagOn( pFcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+
+ if ( !BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ pFcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &pFcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse( IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+
+ if ( NT_SUCCESS( Status ) ) {
+ AttributesAreValid = TRUE;
+ }
+ }
+
+ } else {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_INFO_ATTRIBUTES,
+ pFcb->Vcb->Specific.Disk.VolumeNumber,
+ pFcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &pFcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse( IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e",
+ 4,
+ &Attributes );
+
+ if ( NT_SUCCESS( Status ) ) {
+ AttributesAreValid = TRUE;
+ }
+
+ }
+
+ }
+
+ } else {
+
+ Attributes = pFcb->NonPagedFcb->Attributes;
+ AttributesAreValid = TRUE;
+ }
+
+ if ( !AttributesAreValid ) {
+ DebugTrace( 0, Dbg, "Couldn't get valid attributes for this file.\n", 0 );
+ goto ReleaseAllAndExit;
+ }
+
+ //
+ // Do the set with the shareable bit on!
+ //
+
+ if ( BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait( IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDW--WW==WW==_W_bDbC",
+ NCP_LFN_SET_INFO,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_SET_INFO_ATTRIBUTES,
+ Attributes | 0x80,
+ 0,
+ 0,
+ 0,
+ 0,
+ 8,
+ 0,
+ 8,
+ pFcb->Vcb->Specific.Disk.VolumeNumber,
+ pFcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &pFcb->RelativeFileName );
+
+ } else {
+
+ Status = ExchangeWithWait( IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ Attributes | 0x80,
+ pFcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &pFcb->RelativeFileName );
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Failed to set the shareable attribute on the file.\n", 0 );
+ ASSERT( FALSE && "File NOT marked as shareable!!" );
+ } else {
+ DebugTrace( 0, Dbg, "Shareable bit successfully set.\n", 0 );
+ ClearFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE );
+ }
+
+ReleaseAllAndExit:
+
+ NwReleaseFcb( pFcb->NonPagedFcb );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+ return;
+}