diff options
Diffstat (limited to 'private/nw/rdr/strucsup.c')
-rw-r--r-- | private/nw/rdr/strucsup.c | 3127 |
1 files changed, 3127 insertions, 0 deletions
diff --git a/private/nw/rdr/strucsup.c b/private/nw/rdr/strucsup.c new file mode 100644 index 000000000..3595cc709 --- /dev/null +++ b/private/nw/rdr/strucsup.c @@ -0,0 +1,3127 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + strucsup.c + +Abstract: + + This module implements the Netware Redirector structure support routines. + +Author: + + Manny Weiser (mannyw) 10-Feb-1993 + +Revision History: + +--*/ +#include "procs.h" + +BOOLEAN +GetLongNameSpaceForVolume( + IN PIRP_CONTEXT IrpContext, + IN UNICODE_STRING ShareName, + OUT PCHAR VolumeLongNameSpace, + OUT PCHAR VolumeNumber + ); + +CHAR +GetNewDriveNumber ( + IN PSCB Scb + ); + +VOID +FreeDriveNumber( + IN PSCB Scb, + IN CHAR DriveNumber + ); + +#define Dbg (DEBUG_TRACE_STRUCSUP) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, NwInitializeRcb ) +#pragma alloc_text( PAGE, NwDeleteRcb ) +#pragma alloc_text( PAGE, NwCreateIcb ) +#pragma alloc_text( PAGE, NwDeleteIcb ) +#pragma alloc_text( PAGE, NwVerifyIcb ) +#pragma alloc_text( PAGE, NwVerifyIcbSpecial ) +#pragma alloc_text( PAGE, NwInvalidateAllHandlesForScb ) +#pragma alloc_text( PAGE, NwVerifyScb ) +#pragma alloc_text( PAGE, NwCreateFcb ) +#pragma alloc_text( PAGE, NwFindFcb ) +#pragma alloc_text( PAGE, NwDereferenceFcb ) +#pragma alloc_text( PAGE, NwFindVcb ) +#pragma alloc_text( PAGE, NwCreateVcb ) +#pragma alloc_text( PAGE, NwReopenVcbHandlesForScb ) +#pragma alloc_text( PAGE, NwReopenVcbHandle ) +#ifdef NWDBG +#pragma alloc_text( PAGE, NwReferenceVcb ) +#endif +#pragma alloc_text( PAGE, NwDereferenceVcb ) +#pragma alloc_text( PAGE, NwCleanupVcb ) +#pragma alloc_text( PAGE, GetLongNameSpaceForVolume ) +#pragma alloc_text( PAGE, IsFatNameValid ) +#pragma alloc_text( PAGE, GetNewDriveNumber ) +#pragma alloc_text( PAGE, FreeDriveNumber ) + +#ifndef QFE_BUILD +#pragma alloc_text( PAGE1, NwInvalidateAllHandles ) +#pragma alloc_text( PAGE1, NwCloseAllVcbs ) +#endif + +#endif + +#if 0 // Not pageable + +// see ifndef QFE_BUILD above + +#endif + +VOID +NwInitializeRcb ( + IN PRCB Rcb + ) + +/*++ + +Routine Description: + + This routine initializes new Rcb record. + +Arguments: + + Rcb - Supplies the address of the Rcb record being initialized. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwInitializeRcb, Rcb = %08lx\n", (ULONG)Rcb); + + // + // We start by first zeroing out all of the RCB, this will guarantee + // that any stale data is wiped clean. + // + + RtlZeroMemory( Rcb, sizeof(RCB) ); + + // + // Set the node type code, node byte size, and reference count. + // + + Rcb->NodeTypeCode = NW_NTC_RCB; + Rcb->NodeByteSize = sizeof(RCB); + Rcb->OpenCount = 0; + + // + // Initialize the resource variable for the RCB. + // + + ExInitializeResource( &Rcb->Resource ); + + // + // Initialize the server name and file name tables. + // + + RtlInitializeUnicodePrefix( &Rcb->ServerNameTable ); + RtlInitializeUnicodePrefix( &Rcb->VolumeNameTable ); + RtlInitializeUnicodePrefix( &Rcb->FileNameTable ); + + // + // Return to the caller. + // + + DebugTrace(-1, Dbg, "NwInitializeRcb -> VOID\n", 0); + + return; +} + + +VOID +NwDeleteRcb ( + IN PRCB Rcb + ) + +/*++ + +Routine Description: + + This routine removes the RCB record from our in-memory data + structures. It also will remove all associated underlings + (i.e., FCB records). + +Arguments: + + Rcb - Supplies the Rcb to be removed + +Return Value: + + None + +--*/ + +{ + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwDeleteRcb, Rcb = %08lx\n", (ULONG)Rcb); + + // + // Uninitialize the resource variable for the RCB. + // + + ExDeleteResource( &Rcb->Resource ); + + // + // Return to the caller. + // + + DebugTrace(-1, Dbg, "NwDeleteRcb -> VOID\n", 0); + + return; +} + + +PICB +NwCreateIcb ( + IN USHORT Type, + IN PVOID Associate + ) + +/*++ + +Routine Description: + + This routine allocates and initialize a new ICB. The ICB is + inserted into the FCB's list. + + *** This routine must be called with the RCB held exclusively. + +Arguments: + + Type - The type of ICB this will be. + + Associate - A pointer to an associated data structure. + It will be a FCB, DCB, or SCB. + +Return Value: + + ICB - A pointer to the newly created ICB. + + If memory allocation fails, this routine will raise an exception. + +--*/ + +{ + PICB Icb; + PSCB Scb; + + PAGED_CODE(); + + Icb = ALLOCATE_POOL_EX( NonPagedPool, sizeof( ICB ) ); + + RtlZeroMemory( Icb, sizeof( ICB ) ); + + Icb->NodeTypeCode = Type; + Icb->NodeByteSize = sizeof( ICB ); + Icb->State = ICB_STATE_OPEN_PENDING; + Icb->Pid = (UCHAR)INVALID_PID; + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + if ( Type == NW_NTC_ICB ) { + + PFCB Fcb = (PFCB)Associate; + + // + // Insert this ICB on the list of ICBs for this FCB. + // + + InsertTailList( &Fcb->IcbList, &Icb->ListEntry ); + ++Fcb->IcbCount; + Icb->SuperType.Fcb = Fcb; + Icb->NpFcb = Fcb->NonPagedFcb; + + Fcb->Vcb->OpenFileCount++; + Scb = Fcb->Scb; + + Scb->OpenFileCount++; + + } else if ( Type == NW_NTC_ICB_SCB ) { + + Scb = (PSCB)Associate; + + // + // Insert this ICB on the list of ICBs for this SCB. + // + + InsertTailList( &Scb->IcbList, &Icb->ListEntry ); + ++Scb->IcbCount; + Icb->SuperType.Scb = Scb; + + } else { + + KeBugCheck( RDR_FILE_SYSTEM ); + + } + + NwReleaseRcb( &NwRcb ); + + NwReferenceScb( Scb->pNpScb ); + return( Icb ); +} + + +VOID +NwDeleteIcb ( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PICB Icb + ) + +/*++ + +Routine Description: + + This routine deletes an ICB in the OPEN_PENDING state. + + *** The IRP context must be at the head of the SCB queue when + this routine is called. + +Arguments: + + Icb - A pointer the ICB to delete. + +Return Value: + + None. + +--*/ + +{ + PFCB Fcb; + PSCB Scb; + + PAGED_CODE(); + + // + // Acquire the lock to protect the ICB list. + // + DebugTrace( 0, DEBUG_TRACE_ICBS, "NwDeleteIcb, Icb = %08lx\n", (ULONG)Icb); + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + RemoveEntryList( &Icb->ListEntry ); + + if ( Icb->NodeTypeCode == NW_NTC_ICB ) { + + Fcb = Icb->SuperType.Fcb; + Scb = Fcb->Scb; + + // + // Decrement the open file count for the VCB. Note that the ICB + // only reference the VCB indirectly via the FCB, so that we do + // not dereference the VCB here. + // + + --Fcb->Vcb->OpenFileCount; + --Scb->OpenFileCount; + + // + // Dereference the FCB. This frees the FCB if + // this was the last ICB for the FCB. + // + + NwDereferenceFcb( IrpContext, Fcb ); + + } else if ( Icb->NodeTypeCode == NW_NTC_ICB_SCB ) { + + Scb = Icb->SuperType.Scb; + + // + // Decrement of OpenIcb count on the SCB. + // + + Scb->IcbCount--; + + } else { + KeBugCheck( RDR_FILE_SYSTEM ); + } + + // + // Free the query template buffers. + // + + RtlFreeOemString( &Icb->NwQueryTemplate ); + + if ( Icb->UQueryTemplate.Buffer != NULL ) { + FREE_POOL( Icb->UQueryTemplate.Buffer ); + } + + // + // Try and gracefully catch a 16 bit app closing a + // handle to the server and wipe the connection as + // soon as possible. This only applies to bindery + // authenticated connections because in NDS land, + // we handle the licensing of the connection + // dynamically. + // + + if ( ( Scb->pNpScb->Reference == 1 ) && + ( Icb->NodeTypeCode == NW_NTC_ICB_SCB ) && + ( !Icb->IsTreeHandle ) && + ( IrpContext != NULL ) && + ( Scb->UserName.Length != 0 ) ) + { + LARGE_INTEGER Now; + KeQuerySystemTime( &Now ); + + DebugTrace( 0, Dbg, "Quick disconnecting 16-bit app.\n", 0 ); + + NwAppendToQueueAndWait( IrpContext ); + + if ( Scb->OpenFileCount == 0 && + Scb->pNpScb->State != SCB_STATE_RECONNECT_REQUIRED && + !Scb->PreferredServer ) { + + NwLogoffAndDisconnect( IrpContext, Scb->pNpScb); + } + + Now.QuadPart += ( NwOneSecond * DORMANT_SCB_KEEP_TIME ); + + NwDequeueIrpContext( IrpContext, FALSE ); + NwDereferenceScb( Scb->pNpScb ); + DisconnectTimedOutScbs(Now) ; + CleanupScbs(Now); + + } else { + + NwDereferenceScb( Scb->pNpScb ); + + } + FREE_POOL( Icb ); + NwReleaseRcb( &NwRcb ); +} + +VOID +NwVerifyIcb ( + IN PICB Icb + ) + +/*++ + +Routine Description: + + This routine verifies that an ICB is in the opened state. + If it is not, the routine raises an exception. + +Arguments: + + Icb - A pointer the ICB to verify. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + if ( Icb->State != ICB_STATE_OPENED ) { + ExRaiseStatus( STATUS_INVALID_HANDLE ); + } +} + +VOID +NwVerifyIcbSpecial ( + IN PICB Icb + ) + +/*++ + +Routine Description: + + This routine verifies that an ICB is in the opened state. + If it is not, the routine raises an exception. + +Arguments: + + Icb - A pointer the ICB to verify. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + if ( (Icb->State != ICB_STATE_OPENED && + Icb->State != ICB_STATE_CLEANED_UP) ) { + ExRaiseStatus( STATUS_INVALID_HANDLE ); + } +} + + +ULONG +NwInvalidateAllHandles ( + PLARGE_INTEGER Uid OPTIONAL, + PIRP_CONTEXT IrpContext OPTIONAL + ) + +/*++ + +Routine Description: + + This routine finds all of the ICB in the system that were created + by the user specified by the Logon credentials and marks them + invalid. + +Arguments: + + Uid - Supplies the userid of the handles to close or NULL if all + handles to be invalidated. + IrpContext - The Irpcontext to be used for the NwLogoffAndDisconnect + call, if appropriate. If this is NULL, it indicates a RAS + transition. + +Return Value: + + The number of active handles that were closed. + +--*/ + +{ + KIRQL OldIrql; + PLIST_ENTRY ScbQueueEntry, NextScbQueueEntry; + PNONPAGED_SCB pNpScb; + PSCB pScb; + ULONG FilesClosed = 0; + + PAGED_CODE(); + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + + for (ScbQueueEntry = ScbQueue.Flink ; + ScbQueueEntry != &ScbQueue ; + ScbQueueEntry = NextScbQueueEntry ) { + + pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks ); + + pScb = pNpScb->pScb; + if ( pScb != NULL ) { + + NwReferenceScb( pNpScb ); + + // + // Release the SCB spin lock as we are about to touch nonpaged pool. + // + + KeReleaseSpinLock( &ScbSpinLock, OldIrql ); + + if ((Uid == NULL) || + ( pScb->UserUid.QuadPart == (*Uid).QuadPart)) { + + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + FilesClosed += NwInvalidateAllHandlesForScb( pScb ); + NwReleaseRcb( &NwRcb ); + + if ( IrpContext ) { + + IrpContext->pNpScb = pNpScb; + NwLogoffAndDisconnect( IrpContext , pNpScb); + NwDequeueIrpContext( IrpContext, FALSE ); + + } else { + + // + // No IrpContext means that a RAS transition has occurred. + // Let's try to keep our Netware servers happy if the net + // is still attached. + // + + PIRP_CONTEXT LocalIrpContext; + if (NwAllocateExtraIrpContext(&LocalIrpContext, pNpScb)) { + + // Lock down so that we can send a packet. + NwReferenceUnlockableCodeSection(); + + LocalIrpContext->pNpScb = pNpScb; + NwLogoffAndDisconnect( LocalIrpContext, pNpScb); + + NwAppendToQueueAndWait( LocalIrpContext ); + + NwDequeueIrpContext( LocalIrpContext, FALSE ); + NwDereferenceUnlockableCodeSection (); + NwFreeExtraIrpContext( LocalIrpContext ); + + } + + // + // Clear the LIP data speed. + // + + pNpScb->LipDataSpeed = 0; + pNpScb->State = SCB_STATE_ATTACHING; + + } + + + } + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + + NwDereferenceScb( pNpScb ); + } + + NextScbQueueEntry = pNpScb->ScbLinks.Flink; + } + + KeReleaseSpinLock( &ScbSpinLock, OldIrql ); + + return( FilesClosed ); +} + +ULONG +NwInvalidateAllHandlesForScb ( + PSCB Scb + ) +/*++ + +Routine Description: + + This routine finds all of the ICB in for an SCB and marks them + invalid. + + *** The caller must own the RCB shared or exclusive. + +Arguments: + + SCB - A pointer to the SCB whose files are closed. + +Return Value: + + The number of files that were closed. + +--*/ + +{ + PLIST_ENTRY VcbQueueEntry; + PLIST_ENTRY FcbQueueEntry; + PLIST_ENTRY IcbQueueEntry; + PVCB pVcb; + PFCB pFcb; + PICB pIcb; + + ULONG FilesClosed = 0; + + PAGED_CODE(); + + // + // Walk the list of VCBs for this SCB + // + + for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink; + VcbQueueEntry != &Scb->ScbSpecificVcbQueue; + VcbQueueEntry = VcbQueueEntry->Flink ) { + + pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry ); + + if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + pVcb->Specific.Disk.Handle = (CHAR)-1; + } + + // + // Walk the list of FCBs and DCSs for this VCB + // + + for ( FcbQueueEntry = pVcb->FcbList.Flink; + FcbQueueEntry != &pVcb->FcbList; + FcbQueueEntry = FcbQueueEntry->Flink ) { + + pFcb = CONTAINING_RECORD( FcbQueueEntry, FCB, FcbListEntry ); + + // + // Walk the list of ICBs for this FCB or DCB + // + + for ( IcbQueueEntry = pFcb->IcbList.Flink; + IcbQueueEntry != &pFcb->IcbList; + IcbQueueEntry = IcbQueueEntry->Flink ) { + + pIcb = CONTAINING_RECORD( IcbQueueEntry, ICB, ListEntry ); + + // + // Mark the ICB handle invalid. + // + + pIcb->State = ICB_STATE_CLOSE_PENDING; + pIcb->HasRemoteHandle = FALSE; + FilesClosed++; + } + } + } + + return( FilesClosed ); +} + + +VOID +NwVerifyScb ( + IN PSCB Scb + ) + +/*++ + +Routine Description: + + This routine verifies that an SCB is in the opened state. + If it is not, the routine raises an exception. + +Arguments: + + Scb - A pointer the SCB to verify. + +Return Value: + + None. + +--*/ + +{ + PAGED_CODE(); + + if ( Scb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) { + ExRaiseStatus( STATUS_INVALID_HANDLE ); + } +} + + +PFCB +NwCreateFcb ( + IN PUNICODE_STRING FileName, + IN PSCB Scb, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine allocates and initialize a new FCB. The FCB is + inserted into the RCB prefix table. + + *** This routine must be called with the RCB held exclusively. + +Arguments: + + FileName - The name of the file to create. + + Scb - A pointer to the SCB for this file. + + Vcb - A pointer to the VCB for the file. + +Return Value: + + FCB - A pointer to the newly created DCB. + + If memory allocation fails, this routine will raise an exception. + +--*/ + +{ + PFCB Fcb; + PNONPAGED_FCB NpFcb; + PWCH FileNameBuffer; + SHORT Length; + + PAGED_CODE(); + + Fcb = NULL; + NpFcb = NULL; + + try { + + // + // Allocate and initialize structures. + // + + Fcb = ALLOCATE_POOL_EX( + PagedPool, + sizeof( FCB ) + FileName->Length + sizeof(WCHAR)); + + RtlZeroMemory( Fcb, sizeof( FCB ) ); + Fcb->NodeTypeCode = NW_NTC_FCB; + Fcb->NodeByteSize = sizeof( FCB ) + FileName->Length; + Fcb->State = FCB_STATE_OPEN_PENDING; + + InitializeListHead( &Fcb->IcbList ); + + Fcb->Vcb = Vcb; + Fcb->Scb = Scb; + + FileNameBuffer = (PWCH)(Fcb + 1); + + NpFcb = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NONPAGED_FCB ) ); + RtlZeroMemory( NpFcb, sizeof( NONPAGED_FCB ) ); + + NpFcb->Header.NodeTypeCode = NW_NTC_NONPAGED_FCB; + NpFcb->Header.NodeByteSize = sizeof( NONPAGED_FCB ); + + NpFcb->Fcb = Fcb; + Fcb->NonPagedFcb = NpFcb; + + // + // Initialize the resource variable for the FCB. + // + + ExInitializeResource( &NpFcb->Resource ); + + // + // Copy the file name + // + + RtlCopyMemory( FileNameBuffer, FileName->Buffer, FileName->Length ); + Fcb->FullFileName.MaximumLength = FileName->Length; + Fcb->FullFileName.Length = FileName->Length; + Fcb->FullFileName.Buffer = FileNameBuffer; + + // + // The Relative name is normally the full name without the + // server and volume name. Also strip the leading backslash. + // + + Length = FileName->Length - Vcb->Name.Length - sizeof(L'\\'); + if ( Length < 0 ) { + Length = 0; + } + + Fcb->RelativeFileName.Buffer = (PWCH) + ((PCHAR)FileNameBuffer + Vcb->Name.Length + sizeof(L'\\')); + + Fcb->RelativeFileName.MaximumLength = Length; + Fcb->RelativeFileName.Length = Length; + + // + // Insert this file in the prefix table. + // + + RtlInsertUnicodePrefix( + &NwRcb.FileNameTable, + &Fcb->FullFileName, + &Fcb->PrefixEntry ); + + // + // Insert this file into the VCB list, and increment the + // file open count. + // + + NwReferenceVcb( Vcb ); + + InsertTailList( + &Vcb->FcbList, + &Fcb->FcbListEntry ); + + // + // Initialize the list of file locks for this FCB. + // + + InitializeListHead( &NpFcb->FileLockList ); + InitializeListHead( &NpFcb->PendingLockList ); + + // + // Set the long name bit if necessary + // + + if ( Fcb->Vcb->Specific.Disk.LongNameSpace != LFN_NO_OS2_NAME_SPACE ) { + + // + // OBSCURE CODE POINT + // + // By default FavourLongNames is not set and we use DOS name + // space unless we know we have to use LFN. Reason is if we + // start using LFN then DOS apps that dont handle longnames + // will give us short names and we are hosed because we are + // using LFN NCPs that dont see the short names. Eg. without + // the check below, the following will fail (assume mv.exe is + // DOS app). + // + // cd public\longnamedir + // mv foo bar + // + // This is because we will get call with public\longname\foo + // and the truncated dir name is not accepted. If user values + // case sensitivity, they can set this reg value and we will + // use LFN even for short names. They sacrifice the scenario + // above. + // + if ( FavourLongNames || !IsFatNameValid( &Fcb->RelativeFileName ) ) { + + SetFlag( Fcb->Flags, FCB_FLAGS_LONG_NAME ); + } + } + + } finally { + if ( AbnormalTermination() ) { + if ( Fcb != NULL ) FREE_POOL( Fcb ); + if ( NpFcb != NULL ) FREE_POOL( NpFcb ); + } + } + + return( Fcb ); +} + + +PFCB +NwFindFcb ( + IN PSCB Scb, + IN PVCB Vcb, + IN PUNICODE_STRING FileName, + IN PDCB Dcb OPTIONAL + ) + +/*++ + +Routine Description: + + This routine find an existing FCB by matching the file name. + If a match is find the FCB reference count is incremented. + If no match is found an FCB is created. + +Arguments: + + Scb - A pointer to the server for this open. + + FileName - The name of the file to find. + + Dcb - A pointer to the DCB for relative opens. If NULL the FileName + is an full path name. If non NUL the FileName is relative to + this directory. + + +Return Value: + + FCB - A pointer to the found or newly created DCB. + + If memory allocation fails, this routine will raise an exception. + +--*/ + +{ + PFCB Fcb; + PUNICODE_PREFIX_TABLE_ENTRY Prefix; + UNICODE_STRING FullName; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwFindFcb\n", 0); + ASSERT( Scb->NodeTypeCode == NW_NTC_SCB ); + + if ( Dcb == NULL ) { + + MergeStrings( &FullName, + &Scb->UnicodeUid, + FileName, + PagedPool ); + + } else { + + // + // Construct full name. + // + + FullName.Length = Dcb->FullFileName.Length + FileName->Length + 2; + FullName.MaximumLength = FullName.Length; + FullName.Buffer = ALLOCATE_POOL_EX( PagedPool, FullName.Length ); + + RtlCopyMemory( + FullName.Buffer, + Dcb->FullFileName.Buffer, + Dcb->FullFileName.Length ); + + FullName.Buffer[ Dcb->FullFileName.Length / sizeof(WCHAR) ] = L'\\'; + + RtlCopyMemory( + FullName.Buffer + Dcb->FullFileName.Length / sizeof(WCHAR) + 1, + FileName->Buffer, + FileName->Length ); + } + + DebugTrace( 0, Dbg, " ->FullName = ""%wZ""\n", &FullName); + + // + // Strip the trailing '\' if there is one. + // + + if ( FullName.Buffer[ FullName.Length/sizeof(WCHAR) - 1] == L'\\' ) { + FullName.Length -= sizeof(WCHAR); + } + + Fcb = NULL; + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + Prefix = RtlFindUnicodePrefix( &NwRcb.FileNameTable, &FullName, 0 ); + + if ( Prefix != NULL ) { + Fcb = CONTAINING_RECORD( Prefix, FCB, PrefixEntry ); + + if ( Fcb->FullFileName.Length != FullName.Length ) { + + // + // This was not an exact match. Ignore it. + // or + // This Fcb is for a share owned by another LogonId. + // + + Fcb = NULL; + } + + } + + try { + if ( Fcb != NULL ) { + DebugTrace(0, Dbg, "Found existing FCB = %08lx\n", Fcb); + } else { + Fcb = NwCreateFcb( &FullName, Scb, Vcb ); + DebugTrace(0, Dbg, "Created new FCB = %08lx\n", Fcb); + } + } finally { + + if ( FullName.Buffer != NULL ) { + FREE_POOL( FullName.Buffer ); + } + + NwReleaseRcb( &NwRcb ); + } + + ASSERT( Fcb == NULL || Fcb->Scb == Scb ); + + DebugTrace(-1, Dbg, "NwFindFcb\n", 0); + return( Fcb ); +} + + +VOID +NwDereferenceFcb( + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN PFCB Fcb + ) + +/*++ + +Routine Description: + + This routine decrement the ICB count for an FCB. If the count + goes to zero, cleanup the FCB. + + *** This routine must be called with the RCB held exclusively. + +Arguments: + + FCB - A pointer to an FCB. + +Return Value: + + None. + +--*/ + +{ + PNONPAGED_FCB NpFcb; + PLIST_ENTRY listEntry, nextListEntry; + PNW_FILE_LOCK pFileLock; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwDereferenceFcb\n", 0); + DebugTrace(0, Dbg, "New ICB count = %d\n", Fcb->IcbCount-1 ); + + ASSERT( NodeType( Fcb ) == NW_NTC_FCB || + NodeType( Fcb ) == NW_NTC_DCB ); + + if ( --Fcb->IcbCount == 0 ) { + + NpFcb = Fcb->NonPagedFcb; + + ASSERT( IsListEmpty( &Fcb->IcbList ) ); + + // + // If there are outstanding locks, clean them up. This + // happens when something causes a remote handle to get + // closed before the cleanup routine is called by the + // ios on the regular close path. + // + + if ( !IsListEmpty( &NpFcb->FileLockList ) ) { + + DebugTrace( 0, Dbg, "Freeing stray locks on FCB %08lx\n", NpFcb ); + + for ( listEntry = NpFcb->FileLockList.Flink; + listEntry != &NpFcb->FileLockList; + listEntry = nextListEntry ) { + + nextListEntry = listEntry->Flink; + + pFileLock = CONTAINING_RECORD( listEntry, + NW_FILE_LOCK, + ListEntry ); + + RemoveEntryList( listEntry ); + FREE_POOL( pFileLock ); + } + } + + if ( !IsListEmpty( &NpFcb->PendingLockList ) ) { + + DebugTrace( 0, Dbg, "Freeing stray pending locks on FCB %08lx\n", NpFcb ); + + for ( listEntry = NpFcb->PendingLockList.Flink; + listEntry != &NpFcb->PendingLockList; + listEntry = nextListEntry ) { + + nextListEntry = listEntry->Flink; + + pFileLock = CONTAINING_RECORD( listEntry, + NW_FILE_LOCK, + ListEntry ); + + RemoveEntryList( listEntry ); + FREE_POOL( pFileLock ); + } + } + + // + // Delete the file now, if it is delete pending. + // + + if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE ) ) { + NwDeleteFile( IrpContext ); + } + + // + // Remove this file in the prefix table. + // + + RtlRemoveUnicodePrefix( + &NwRcb.FileNameTable, + &Fcb->PrefixEntry ); + + // + // Remove this file from the SCB list, and decrement the + // file open count. + // + + RemoveEntryList( &Fcb->FcbListEntry ); + NwDereferenceVcb( Fcb->Vcb, IrpContext, TRUE ); + + // + // Delete the resource variable for the FCB. + // + + ExDeleteResource( &NpFcb->Resource ); + + // + // Delete the cache buffer and MDL. + // + + if ( NpFcb->CacheBuffer != NULL ) { + FREE_POOL( NpFcb->CacheBuffer ); + FREE_MDL( NpFcb->CacheMdl ); + } + + // + // Finally free the paged and non-paged memory + // + + FREE_POOL( Fcb ); + FREE_POOL( NpFcb ); + } + + DebugTrace(-1, Dbg, "NwDereferenceFcb\n", 0); +} + + +PVCB +NwFindVcb ( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING VolumeName, + IN ULONG ShareType, + IN WCHAR DriveLetter, + IN BOOLEAN ExplicitConnection, + IN BOOLEAN FindExisting + ) + +/*++ + +Routine Description: + + This routine looks for a VCB structure. If one is found, it + is referenced and a pointer is returned. If no VCB is found, an + attempt is made to connect to the named volume and to create a VCB. + +Arguments: + + IrpContext - A pointer to the IRP context block for this request. + + VolumeName - The minimum name of the volume. This will be in one of + the following forms: + + \SERVER\SHARE UNC open server volume + \TREE\VOLUME UNC open tree volume in current context + \TREE\PATH.TO.VOLUME UNC open distinguished tree volume + + \X:\SERVER\SHARE tree connect server volume + \X:\TREE\VOLUME tree connect tree volume in current context + \X:\TREE\PATH.TO.VOLUME tree connect distinguished tree volume + + ShareType - The type of the share to find. + + DriveLetter - The drive letter to find. A - Z for drive letter, 1 - 9 + for LPT ports or 0 if none. + + ExplicitConnection - If TRUE, the caller is make an explicit connection + to this Volume. If FALSE, this is an implicit connection made by + a UNC operation. + +Return Value: + + VCB - Pointer to a found or newly created VCB. + +--*/ +{ + PVCB Vcb = NULL; + BOOLEAN OwnRcb = TRUE; + PUNICODE_PREFIX_TABLE_ENTRY Prefix; + UNICODE_STRING UidVolumeName; + PNONPAGED_SCB pNpScb = IrpContext->pScb->pNpScb; + + PAGED_CODE(); + + UidVolumeName.Buffer = NULL; + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + try { + + MergeStrings( &UidVolumeName, + &IrpContext->pScb->UnicodeUid, + VolumeName, + PagedPool ); + + DebugTrace(+1, Dbg, "NwFindVcb %wZ\n", &UidVolumeName ); + + if ( DriveLetter != 0 ) { + + // + // This is a drive relative path. Look up the drive letter. + // + + ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) || + ( DriveLetter >= L'1' && DriveLetter <= L'9' ) ); + + if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) { + Vcb = DriveMapTable[DriveLetter - L'A']; + } else { + Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1']; + } + + // + // Was the Vcb created for this user? + // + + if ((Vcb != NULL) && + (IrpContext->Specific.Create.UserUid.QuadPart != Vcb->Scb->UserUid.QuadPart )) { + + ExRaiseStatus( STATUS_ACCESS_DENIED ); + } + + } else { + + // + // This is a UNC path. Look up the path name. + // + + Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 ); + + if ( Prefix != NULL ) { + Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry ); + + if ( Vcb->Name.Length != UidVolumeName.Length ) { + + // + // This was not an exact match. Ignore it. + // + + Vcb = NULL; + } + } + } + + if ( Vcb != NULL ) { + + // + // If this is an explicit use to a UNC path, we may find an + // existing VCB structure. Mark this structure, and reference it. + // + + if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) && + ExplicitConnection ) { + + NwReferenceVcb( Vcb ); + SetFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ); + SetFlag( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY ); + + // + // Count this as an open file on the SCB. + // + + ++Vcb->Scb->OpenFileCount; + } + + NwReferenceVcb( Vcb ); + DebugTrace(0, Dbg, "Found existing VCB = %08lx\n", Vcb); + + // + // If this VCB is queued to a different SCB as may + // happen when we are resolving NDS UNC names, we + // need to re-point the irpcontext at the correct SCB. + // We can't hold the RCB or the open lock while we do + // this! + // + // It is ok to release the open lock since we know + // that we have an already created VCB and that we're + // not creating a new vcb. + // + + if ( Vcb->Scb != IrpContext->pScb ) { + + NwReferenceScb( Vcb->Scb->pNpScb ); + + NwReleaseOpenLock( ); + + NwReleaseRcb( &NwRcb ); + OwnRcb = FALSE; + + NwDequeueIrpContext( IrpContext, FALSE ); + NwDereferenceScb( IrpContext->pNpScb ); + + IrpContext->pScb = Vcb->Scb; + IrpContext->pNpScb = Vcb->Scb->pNpScb; + + NwAppendToQueueAndWait( IrpContext ); + + NwAcquireOpenLock( ); + + } + + } else if ( !FindExisting ) { + + // + // Can't hold the RCB while creating a new VCB. + // + + NwReleaseRcb( &NwRcb ); + OwnRcb = FALSE; + + Vcb = NwCreateVcb( + IrpContext, + IrpContext->pScb, + &UidVolumeName, + ShareType, + DriveLetter, + ExplicitConnection ); + + if ( Vcb ) { + DebugTrace(0, Dbg, "Created new VCB = %08lx\n", Vcb); + } + + } else { + + // + // If we didn't find anything and don't want + // to do a create, make sure the caller doesn't + // try to process the nds path. + // + + IrpContext->Specific.Create.NeedNdsData = FALSE; + } + + } finally { + + if ( OwnRcb ) { + NwReleaseRcb( &NwRcb ); + } + + if (UidVolumeName.Buffer != NULL) { + FREE_POOL( UidVolumeName.Buffer ); + } + } + + DebugTrace(-1, Dbg, "NwFindVcb\n", 0); + return( Vcb ); + +} + +PVCB +NwCreateVcb ( + IN PIRP_CONTEXT IrpContext, + IN PSCB Scb, + IN PUNICODE_STRING VolumeName, + IN ULONG ShareType, + IN WCHAR DriveLetter, + IN BOOLEAN ExplicitConnection + ) + +/*++ + +Routine Description: + + This routine allocates and initialize a new VCB. The + workstation tries to connect to the Volume. If successful + it creates a VCB and it is inserted into the volume + prefix table. + +Arguments: + + IrpContext - A pointer to IRP context information. + + Scb - A pointer to the SCB for this volume. + + VolumeName - The name of the volume to create. + + ShareType - The type of share to create. + + DriveLetter - The drive letter assigned to this volume, or 0 if none. + + ExplicitConnection - TRUE if we are creating this VCB due to an + add connection request. FALSE if we are creating the VCB to + service a UNC request. + +Return Value: + + VCB - A pointer to the newly created DCB. + NULL - Could not create a DCB, or failed to connect to the volume. + +--*/ + +{ + PVCB Vcb; + PWCH VolumeNameBuffer; + PWCH ShareNameBuffer; + PWCH ConnectNameBuffer; + UCHAR DirectoryHandle; + ULONG QueueId; + BYTE *pbQueue, *pbRQueue; + BOOLEAN PrintQueue = FALSE; + NTSTATUS Status; + CHAR LongNameSpace = LFN_NO_OS2_NAME_SPACE; + CHAR VolumeNumber = -1; + CHAR DriveNumber = 0; + USHORT PreludeLength, ConnectNameLength; + PNONPAGED_SCB NpScb = Scb->pNpScb; + + UNICODE_STRING ShareName; + UNICODE_STRING LongShareName; + PWCH p; + + BOOLEAN InsertedColon; + BOOLEAN LongName = FALSE; + BOOLEAN LicensedConnection = FALSE; + + PUNICODE_STRING puConnectName; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwCreateVcb\n", 0); + DebugTrace( 0, Dbg, " ->Server = %wZ\n", &NpScb->ServerName ); + DebugTrace( 0, Dbg, " ->VolumeName = %wZ\n", VolumeName ); + DebugTrace( 0, Dbg, " ->DriveLetter = %x\n", DriveLetter ); + + Vcb = NULL; + ShareName.Buffer = NULL; + + if ( IrpContext != NULL && + IrpContext->Specific.Create.NdsCreate ) { + + // + // If we don't have the NDS data for this create, bail out + // and have the create thread get the data before re-attempting + // the create. This is kind of weird, but we have to do it + // so that we handle the open lock correctly and prevent + // duplicate creates. + // + + if ( IrpContext->Specific.Create.NeedNdsData ) { + DebugTrace( -1, Dbg, "NwCreateVcb: Need NDS data to continue.\n", 0 ); + return NULL; + } + + ConnectNameLength = IrpContext->Specific.Create.UidConnectName.Length; + puConnectName = &IrpContext->Specific.Create.UidConnectName; + + } else { + + puConnectName = VolumeName; + ConnectNameLength = 0; + } + + DebugTrace( 0, Dbg, " ->ConnectName = %wZ\n", puConnectName ); + + if ( IrpContext != NULL) { + + // + // Build the share name from the volume name. + // + // The share name will either be 'volume:' or 'volume:path\path' + // + + // + // Allocate space for the share name buffer, and copy the volume + // name to the share name buffer, skipping the server name and + // the leading backslash. + // + + if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) { + + if ( ShareType == RESOURCETYPE_PRINT ) { + ExRaiseStatus( STATUS_BAD_NETWORK_PATH ); + } else if ( ShareType == RESOURCETYPE_ANY) { + ShareType = RESOURCETYPE_DISK; + } + + PreludeLength = Scb->UidServerName.Length + + sizeof( L"X:") + sizeof(WCHAR); + + } else if ( DriveLetter >= L'1' && DriveLetter <= L'9' ) { + + if ( ShareType == RESOURCETYPE_DISK ) { + ExRaiseStatus( STATUS_BAD_NETWORK_PATH ); + } else if ( ShareType == RESOURCETYPE_ANY) { + ShareType = RESOURCETYPE_PRINT; + } + + PreludeLength = Scb->UidServerName.Length + + sizeof( L"LPTX") + sizeof(WCHAR); + + } else { + PreludeLength = Scb->UidServerName.Length + sizeof(WCHAR); + } + + // + // Quick check for bogus volume name. + // + + if ( puConnectName->Length <= PreludeLength ) { + ExRaiseStatus( STATUS_BAD_NETWORK_PATH ); + } + + // + // Clip the NDS share name at the appropriate spot. + // + + if ( IrpContext->Specific.Create.NdsCreate ) { + ShareName.Length = (USHORT)IrpContext->Specific.Create.dwNdsShareLength; + } else { + ShareName.Length = puConnectName->Length - PreludeLength; + } + + ShareName.Buffer = ALLOCATE_POOL_EX( PagedPool, ShareName.Length + sizeof(WCHAR) ); + + RtlMoveMemory( + ShareName.Buffer, + puConnectName->Buffer + PreludeLength / sizeof(WCHAR), + ShareName.Length ); + + ShareName.MaximumLength = ShareName.Length; + + DebugTrace( 0, Dbg, " ->ServerShare = %wZ\n", &ShareName ); + + // + // Create a long share name. + // + + LongShareName.Length = ShareName.Length; + LongShareName.Buffer = puConnectName->Buffer + PreludeLength / sizeof(WCHAR); + + // + // Now scan the share name for the 1st slash. + // + + InsertedColon = FALSE; + + for ( p = ShareName.Buffer; p < ShareName.Buffer + ShareName.Length/sizeof(WCHAR); p++ ) { + if ( *p == L'\\') { + *p = L':'; + InsertedColon = TRUE; + break; + } + } + + if ( !InsertedColon ) { + + // + // We need to append a column to generate the share name. + // Since we already allocated an extra WCHAR of buffer space, + // just append the ':' to the share name. + // + + ShareName.Buffer[ShareName.Length / sizeof(WCHAR)] = L':'; + ShareName.Length += 2; + } + + ASSERT( ShareType == RESOURCETYPE_ANY || + ShareType == RESOURCETYPE_DISK || + ShareType == RESOURCETYPE_PRINT ); + + // + // If there are no vcb's and no nds streams connected to this scb and + // this is a Netware 4.x server that is NDS authenticated, then we + // haven't yet licensed this connection and we should do so. + // + + if ( ( IrpContext->pScb->MajorVersion > 3 ) && + ( IrpContext->pScb->UserName.Length == 0 ) && + ( IrpContext->pScb->VcbCount == 0 ) && + ( IrpContext->pScb->OpenNdsStreams == 0 ) ) { + + Status = NdsLicenseConnection( IrpContext ); + + if ( !NT_SUCCESS( Status ) ) { + ExRaiseStatus( STATUS_REMOTE_SESSION_LIMIT ); + } + + LicensedConnection = TRUE; + } + + if ( ShareType == RESOURCETYPE_ANY || + ShareType == RESOURCETYPE_DISK ) { + + GetLongNameSpaceForVolume( + IrpContext, + ShareName, + &LongNameSpace, + &VolumeNumber ); + + // + // BUGBUG: If this is the deref of a directory map, the path we have + // been provided is the short name space path. We have to get the + // long name path to connect up the long name space for the user! + // + + if ( ( IrpContext->Specific.Create.NdsCreate ) && + ( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_DIRMAP ) ) { + LongNameSpace = LFN_NO_OS2_NAME_SPACE; + } + + // + // Try to get a permanent handle to the volume. + // + + if ( LongNameSpace == LFN_NO_OS2_NAME_SPACE ) { + + DriveNumber = GetNewDriveNumber(Scb); + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "SbbJ", + NCP_DIR_FUNCTION, NCP_ALLOCATE_DIR_HANDLE, + 0, + DriveNumber, + &ShareName ); + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nb", + &DirectoryHandle ); + } + + if ( !NT_SUCCESS( Status ) ) { + FreeDriveNumber( Scb, DriveNumber ); + } + + } else { + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWbDbC", + NCP_LFN_ALLOCATE_DIR_HANDLE, + LongNameSpace, + 0, + 0, // Mode = permanent + VolumeNumber, + LFN_FLAG_SHORT_DIRECTORY, + 0xFF, // Flag + &LongShareName ); + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nb", + &DirectoryHandle ); + } + + // + // WARNING. See comment towards end of NwCreateFcb() !!! + // + if ( FavourLongNames || !IsFatNameValid( &LongShareName ) ) { + LongName = TRUE; + } + } + + if ( ( Status == STATUS_NO_SUCH_DEVICE ) && + ( ShareType != RESOURCETYPE_ANY ) ) { + + // + // Asked for disk and it failed. If its ANY, then try print. + // + + if (DriveNumber) { + FreeDriveNumber( Scb, DriveNumber ); + } + + FREE_POOL( ShareName.Buffer ); + + if ( LicensedConnection ) { + NdsUnlicenseConnection( IrpContext ); + } + + ExRaiseStatus( STATUS_BAD_NETWORK_NAME ); + return( NULL ); + } + + } + + if ( ShareType == RESOURCETYPE_PRINT || + ( ShareType == RESOURCETYPE_ANY && !NT_SUCCESS( Status ) ) ) { + + // + // Try to connect to a print queue. If this is a bindery + // server or an nds server with bindery emulation, we scan + // the bindery for the QueueId. Otherwise, the QueueId is + // simply the ds object id with the byte ordering reversed. + // + + ShareName.Length -= sizeof(WCHAR); + + if ( ( Scb->MajorVersion < 4 ) || + ( !( IrpContext->Specific.Create.NdsCreate ) ) ) { + + Status = ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + "SdwJ", // Format string + NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT, + -1, // Previous ID + OT_PRINT_QUEUE, + &ShareName ); // Queue Name + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nd", + &QueueId ); + } + + } else { + + if ( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_QUEUE ) { + + DebugTrace( 0, Dbg, "Mapping NDS print queue %08lx\n", + IrpContext->Specific.Create.dwNdsOid ); + + pbQueue = (BYTE *)&IrpContext->Specific.Create.dwNdsOid; + pbRQueue = (BYTE *)&QueueId; + + pbRQueue[0] = pbQueue[3]; + pbRQueue[1] = pbQueue[2]; + pbRQueue[2] = pbQueue[1]; + pbRQueue[3] = pbQueue[0]; + + Status = STATUS_SUCCESS; + + } else { + + DebugTrace( 0, Dbg, "Nds object is not a print queue.\n", 0 ); + Status = STATUS_UNSUCCESSFUL; + } + } + + PrintQueue = TRUE; + } + + if ( !NT_SUCCESS( Status ) ) { + + if (DriveNumber) { + FreeDriveNumber( Scb, DriveNumber ); + } + + FREE_POOL( ShareName.Buffer ); + + if ( LicensedConnection ) { + NdsUnlicenseConnection( IrpContext ); + } + + ExRaiseStatus( STATUS_BAD_NETWORK_PATH ); + return( NULL ); + } + + } else { + DirectoryHandle = 1; + } + + // + // Allocate and initialize structures. + // + + try { + + Vcb = ALLOCATE_POOL_EX( PagedPool, sizeof( VCB ) + // vcb + VolumeName->Length + // volume name + ShareName.Length + // share name + ConnectNameLength ); // connect name + + RtlZeroMemory( Vcb, sizeof( VCB ) ); + Vcb->NodeTypeCode = NW_NTC_VCB; + Vcb->NodeByteSize = sizeof( VCB ) + + VolumeName->Length + + ShareName.Length + + ConnectNameLength; + + InitializeListHead( &Vcb->FcbList ); + + VolumeNameBuffer = (PWCH)(Vcb + 1); + ShareNameBuffer = (PWCH)((PCHAR)VolumeNameBuffer + VolumeName->Length); + ConnectNameBuffer = (PWCH)((PCHAR)ShareNameBuffer + ShareName.Length); + + Vcb->Reference = 1; + + // + // Copy the volume name + // + + RtlCopyMemory( VolumeNameBuffer, VolumeName->Buffer, VolumeName->Length ); + Vcb->Name.MaximumLength = VolumeName->Length; + Vcb->Name.Length = VolumeName->Length; + Vcb->Name.Buffer = VolumeNameBuffer; + + // + // Copy the share name + // + + if ( IrpContext != NULL) { + + RtlCopyMemory( ShareNameBuffer, ShareName.Buffer, ShareName.Length ); + Vcb->ShareName.MaximumLength = ShareName.Length; + Vcb->ShareName.Length = ShareName.Length; + Vcb->ShareName.Buffer = ShareNameBuffer; + + } + + // + // Copy the connect name + // + + if ( ConnectNameLength ) { + + RtlCopyMemory( ConnectNameBuffer, + IrpContext->Specific.Create.UidConnectName.Buffer, + IrpContext->Specific.Create.UidConnectName.Length ); + Vcb->ConnectName.MaximumLength = IrpContext->Specific.Create.UidConnectName.Length; + Vcb->ConnectName.Length = IrpContext->Specific.Create.UidConnectName.Length; + Vcb->ConnectName.Buffer = ConnectNameBuffer; + + } + + if ( ExplicitConnection ) { + + // + // Bump the reference count to account for this drive being + // mapped via an explicit connection. + // + + NwReferenceVcb( Vcb ); + SetFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ); + SetFlag( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY ); + + } + + if ( LongName ) { + SetFlag( Vcb->Flags, VCB_FLAG_LONG_NAME ); + } + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + if ( DriveLetter != 0) { + + // + // Insert this VCB in the drive map table. + // + + if ( DriveLetter >= 'A' && DriveLetter <= 'Z' ) { + DriveMapTable[DriveLetter - 'A'] = Vcb; + } else { + DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - '1'] = Vcb; + } + + Vcb->DriveLetter = DriveLetter; + + } else { + + // + // Insert this VCB in the prefix table. + // + + RtlInsertUnicodePrefix( + &NwRcb.VolumeNameTable, + &Vcb->Name, + &Vcb->PrefixEntry ); + } + + // + // Add this VCB to the global list. + // + + InsertTailList( &GlobalVcbList, &Vcb->GlobalVcbListEntry ); + Vcb->SequenceNumber = CurrentVcbEntry++; + + // + // Insert this VCB in the per SCB list + // + + Vcb->Scb = Scb; + InsertTailList( &Scb->ScbSpecificVcbQueue, &Vcb->VcbListEntry ); + ++Scb->VcbCount; + NwReferenceScb( Scb->pNpScb ); + + if ( ExplicitConnection ) { + + // + // Count this as an open file on the SCB. + // + + ++Vcb->Scb->OpenFileCount; + } + + if ( !PrintQueue) { + + PLIST_ENTRY VcbQueueEntry; + PVCB pVcb; + + Vcb->Specific.Disk.Handle = DirectoryHandle; + Vcb->Specific.Disk.LongNameSpace = LongNameSpace; + Vcb->Specific.Disk.VolumeNumber = VolumeNumber; + Vcb->Specific.Disk.DriveNumber = DriveNumber; + + // + // Appears that some servers can reuse the same permanent drive handle. + // if this happens we want to make the old handle invalid otherwise + // we will keep on using the new volume as if its the old one. + // + + for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink; + VcbQueueEntry != &Scb->ScbSpecificVcbQueue; + VcbQueueEntry = pVcb->VcbListEntry.Flink ) { + + pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry ); + + if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + + if (( pVcb->Specific.Disk.Handle == DirectoryHandle ) && + ( pVcb->Specific.Disk.VolumeNumber != VolumeNumber )) { + // Invalidate the old handle + pVcb->Specific.Disk.Handle = (CHAR)-1; + + // We could assume that the new one is correct but I don't think we will.... + Vcb->Specific.Disk.Handle = (CHAR)-1; + break; + } + } + } + + } else { + SetFlag( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ); + Vcb->Specific.Print.QueueId = QueueId; + } + + NwReleaseRcb( &NwRcb ); + + } finally { + + if ( AbnormalTermination() ) { + + if ( Vcb != NULL ) FREE_POOL( Vcb ); + + if ( LicensedConnection ) { + NdsUnlicenseConnection( IrpContext ); + } + } + + if ( ShareName.Buffer != NULL ) { + FREE_POOL( ShareName.Buffer ); + } + + DebugTrace(-1, Dbg, "NwCreateVcb %lx\n", Vcb); + } + + return( Vcb ); +} + +VOID +NwReopenVcbHandlesForScb ( + IN PIRP_CONTEXT IrpContext, + IN PSCB Scb + ) + +/*++ + +Routine Description: + + This routine reopens VCB handles after the autoreconnects to a server. + + *** This IrpContext must already be at the head of the SCB queue. + +Arguments: + + IrpContext - A pointer to IRP context information. + + Scb - A pointer to the SCB for this volume. + +Return Value: + + None. + +--*/ + +{ + PLIST_ENTRY VcbQueueEntry, NextVcbQueueEntry; + PVCB pVcb; + + PLIST_ENTRY FcbQueueEntry; + PLIST_ENTRY IcbQueueEntry; + PFCB pFcb; + PICB pIcb; + + NTSTATUS Status; + + PAGED_CODE(); + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + // + // Walk the list of VCBs for this SCB + // + + for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink; + VcbQueueEntry != &Scb->ScbSpecificVcbQueue; + VcbQueueEntry = NextVcbQueueEntry ) { + + pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry ); + + if ( pVcb->Specific.Disk.Handle != 1 ) { + + // + // Skip reconnecting SYS:LOGIN, since we get it for free. + // + + // + // Reference the VCB so it can't disappear on us, then release + // the RCB. + // + + NwReferenceVcb( pVcb ); + NwReleaseRcb( &NwRcb ); + + // + // Try to get a permanent handle to the volume. + // + + if ( BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + + Status = ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + "SdwU", // Format string + NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT, + -1, // Previous ID + OT_PRINT_QUEUE, + &pVcb->ShareName ); // Queue Name + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nd", + &pVcb->Specific.Print.QueueId ); + } + + } else { + + NwReopenVcbHandle( IrpContext, pVcb); + + } + + + // + // Setup for the next loop iteration. + // + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + // + // Walk the list of DCSs for this VCB and make them all valid. + // + + for ( FcbQueueEntry = pVcb->FcbList.Flink; + FcbQueueEntry != &pVcb->FcbList; + FcbQueueEntry = FcbQueueEntry->Flink ) { + + pFcb = CONTAINING_RECORD( FcbQueueEntry, FCB, FcbListEntry ); + + if ( pFcb->NodeTypeCode == NW_NTC_DCB ) { + + // + // Walk the list of ICBs for this FCB or DCB + // + + for ( IcbQueueEntry = pFcb->IcbList.Flink; + IcbQueueEntry != &pFcb->IcbList; + IcbQueueEntry = IcbQueueEntry->Flink ) { + + pIcb = CONTAINING_RECORD( IcbQueueEntry, ICB, ListEntry ); + + // + // Mark the ICB handle invalid. + // + + pIcb->State = ICB_STATE_OPENED; + } + } + } + + } + + NextVcbQueueEntry = VcbQueueEntry->Flink; + + if ( pVcb->Specific.Disk.Handle != 1 ) { + NwDereferenceVcb( pVcb, NULL, TRUE ); + } + + } + + NwReleaseRcb( &NwRcb ); +} + +VOID +NwReopenVcbHandle( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb + ) + +/*++ + +Routine Description: + + This routine reopens a VCB handle after it appears that the server + may have dismounted and remounted the volume. + + *** This IrpContext must already be at the head of the SCB queue. + +Arguments: + + IrpContext - A pointer to IRP context information. + + Vcb - A pointer to the VCB for this volume. + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + + PAGED_CODE(); + + ASSERT( Vcb->Scb->pNpScb->Requests.Flink == &IrpContext->NextRequest ); + + if ( Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ) { + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "SbbJ", + NCP_DIR_FUNCTION, NCP_ALLOCATE_DIR_HANDLE, + 0, + Vcb->Specific.Disk.DriveNumber, + &Vcb->ShareName ); + + } else { + UNICODE_STRING Name; + + PWCH thisChar, lastChar; + + Status = DuplicateUnicodeStringWithString ( + &Name, + &Vcb->ShareName, + PagedPool); + + if ( !NT_SUCCESS( Status ) ) { + // Not much we can do now. + return; + } + + thisChar = Name.Buffer; + lastChar = &Name.Buffer[ Name.Length / sizeof(WCHAR) ]; + + // + // Change the : to a backslash so that FormatMessage works + // + + while ( thisChar < lastChar ) { + if (*thisChar == L':' ) { + *thisChar = L'\\'; + break; + } + thisChar++; + } + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWbDbC", + NCP_LFN_ALLOCATE_DIR_HANDLE, + Vcb->Specific.Disk.LongNameSpace, + 0, + 0, // Mode = permanent + Vcb->Specific.Disk.VolumeNumber, + LFN_FLAG_SHORT_DIRECTORY, + 0xFF, // Flag + &Name ); + + if ( Name.Buffer != NULL ) { + FREE_POOL( Name.Buffer ); + } + + } + + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nb", + &Vcb->Specific.Disk.Handle ); + } + + if ( !NT_SUCCESS( Status ) ) { + Vcb->Specific.Disk.Handle = (CHAR)-1; + } else { + + PLIST_ENTRY VcbQueueEntry; + PVCB pVcb; + + // + // Appears that some servers can reuse the same permanent drive handle. + // if this happens we want to make the old handle invalid otherwise + // we will keep on using the new volume as if its the old one. + // + // Note that we reach the scb pointer from the npscb pointer because + // the scb pointer isn't always valid. These few cases where only one + // pointer is set should be found and fixed. + // + + for ( VcbQueueEntry = IrpContext->pNpScb->pScb->ScbSpecificVcbQueue.Flink; + VcbQueueEntry != &IrpContext->pNpScb->pScb->ScbSpecificVcbQueue; + VcbQueueEntry = pVcb->VcbListEntry.Flink ) { + + pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry ); + + if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + + if (( pVcb->Specific.Disk.Handle == Vcb->Specific.Disk.Handle ) && + ( pVcb->Specific.Disk.VolumeNumber != Vcb->Specific.Disk.VolumeNumber )) { + // Invalidate the old handle + pVcb->Specific.Disk.Handle = (CHAR)-1; + + // We could assume that the new one is correct but I don't think we will.... + Vcb->Specific.Disk.Handle = (CHAR)-1; + break; + } + } + } + } + +} +#ifdef NWDBG + +VOID +NwReferenceVcb ( + IN PVCB Vcb + ) +/*++ + +Routine Description: + + This routine increments the FCB count for a VCB. + +Arguments: + + VCB - A pointer to an VCB. + +Return Value: + + None. + +--*/ + +{ + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwReferenceVcb %08lx\n", Vcb); + DebugTrace(0, Dbg, "Current Reference count = %d\n", Vcb->Reference ); + + ASSERT( NodeType( Vcb ) == NW_NTC_VCB ); + + ++Vcb->Reference; + +} +#endif + + +VOID +NwDereferenceVcb ( + IN PVCB Vcb, + IN PIRP_CONTEXT IrpContext OPTIONAL, + IN BOOLEAN OwnRcb + ) +/*++ + +Routine Description: + + This routine decrement the FCB count for a VCB. + If the count goes to zero, we record the time. The scavenger + thread will cleanup delete the VCB if it remains idle. + + This routine may be called with the RCB owned and the irpcontext + at the head of the queue. Be careful when dequeueing the irp + context or acquiring any resources! + +Arguments: + + VCB - A pointer to an VCB. + +Return Value: + + None. + +--*/ + +{ + PSCB Scb = Vcb->Scb; + PNONPAGED_SCB pOrigNpScb = NULL; + +#ifdef NWDBG + BOOLEAN OwnRcbExclusive = FALSE; +#endif + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwDereferenceVcb %08lx\n", Vcb); + + ASSERT( NodeType( Vcb ) == NW_NTC_VCB ); + +#ifdef NWDBG + + // + // A little extra lock checking. + // + + OwnRcbExclusive = ExIsResourceAcquiredExclusiveLite( &(NwRcb.Resource) ); + + if ( OwnRcb ) { + ASSERT( OwnRcbExclusive ); + } else { + ASSERT( !OwnRcbExclusive ); + } + +#endif + + // + // We have to get to the right scb queue before doing this + // so that CleanupVcb unlicenses the correct connection. + // + + if ( ( IrpContext ) && + ( IrpContext->pNpScb->pScb->MajorVersion > 3 ) && + ( IrpContext->pNpScb != Scb->pNpScb ) ) { + + if ( OwnRcb ) { + NwReleaseRcb( &NwRcb ); + } + + pOrigNpScb = IrpContext->pNpScb; + ASSERT( pOrigNpScb != NULL ); + + NwDequeueIrpContext( IrpContext, FALSE ); + + IrpContext->pScb = Scb; + IrpContext->pNpScb = Scb->pNpScb; + + NwAppendToQueueAndWait( IrpContext ); + + // + // If the caller owned the RCB, we have to make sure + // we re-acquire the RCB reference that we freed for + // them so that they don't lose access to the resource + // too early. + // + + if ( OwnRcb ) { + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + } + + } + + // + // Acquire the lock to protect the Reference count. + // + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + DebugTrace(0, Dbg, "Current Reference count = %d\n", Vcb->Reference ); + --Vcb->Reference; + + if ( Vcb->Reference == 0 ) { + if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY ) || + IrpContext == NULL ) { + + // + // Either this is a UNC path, or we don't have an IRP context + // to do the VCB cleanup. Simply timestamp the VCB and the + // scavenger will cleanup if the VCB remains idle. + // + + KeQuerySystemTime( &Vcb->LastUsedTime ); + NwReleaseRcb( &NwRcb ); + + } else { + + // + // This VCB is being explicitly deleted by the user. + // Make it go away now. This will release the RCB. + // + + NwCleanupVcb( Vcb, IrpContext ); + + } + + } else { + + NwReleaseRcb( &NwRcb ); + } + + // + // At this point, we've released our acquisition of the RCB, but + // the caller may still own the RCB. To prevent a deadlock, we + // have to be careful when we put this irpcontext back on the + // original server. + // + + if ( pOrigNpScb ) { + + if ( OwnRcb ) { + NwReleaseRcb( &NwRcb ); + } + + NwDequeueIrpContext( IrpContext, FALSE ); + + IrpContext->pNpScb = pOrigNpScb; + IrpContext->pScb = pOrigNpScb->pScb; + + NwAppendToQueueAndWait( IrpContext ); + + // + // Re-acquire for the caller. + // + + if ( OwnRcb ) { + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + } + + } + + DebugTrace(-1, Dbg, "NwDereferenceVcb\n", 0); + +} + + +VOID +NwCleanupVcb( + IN PVCB pVcb, + IN PIRP_CONTEXT IrpContext + ) +/*++ + +Routine Description: + + This routine cleans up and frees a VCB. + + This routine must be called with the RCB held to + protect the drive map tables and unicode prefix + tables. The caller must own the IRP context at + the head of the SCB queue. This routine will + free the RCB and dequeue the irp context. + +Arguments: + + pVcb - A pointer to the VCB to free. + +Return Value: + + None. + +--*/ +{ + NTSTATUS Status; + CHAR Handle; + BOOLEAN CallDeleteScb = FALSE; + PSCB pScb = pVcb->Scb; + PNONPAGED_SCB pNpScb = pScb->pNpScb; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "NwCleanupVcb...\n", 0); + + ASSERT( pVcb->NodeTypeCode == NW_NTC_VCB ); + ASSERT( IsListEmpty( &pVcb->FcbList ) ); + ASSERT( pVcb->OpenFileCount == 0 ); + + DebugTrace(0, Dbg, "Cleaning Vcb %08lx\n", pVcb); + + // + // Remove the VCB from the drive map table. The RCB is owned, so + // the drive map table and vcb lists are protected. + // + + if ( pVcb->DriveLetter != 0 ) { + if ( pVcb->DriveLetter >= L'A' && pVcb->DriveLetter <= L'Z' ) { + DriveMapTable[pVcb->DriveLetter - L'A'] = NULL; + } else { + DriveMapTable[MAX_DISK_REDIRECTIONS + pVcb->DriveLetter - L'1'] = NULL; + } + + if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + FreeDriveNumber( pVcb->Scb, pVcb->Specific.Disk.DriveNumber ); + } + } + + // + // Remove the VCB from the Volume Name table. + // + + RtlRemoveUnicodePrefix ( &NwRcb.VolumeNameTable, &pVcb->PrefixEntry ); + + // + // Remove the VCB from the global list + // + + RemoveEntryList( &pVcb->GlobalVcbListEntry ); + + // + // Remove the VCB from our SCB's VCB list. + // + + RemoveEntryList( &pVcb->VcbListEntry ); + + --pScb->VcbCount; + + // + // There is no server jumping allowed!! We should have + // pre-located the correct server to avoid deadlock problems. + // + + ASSERT( IrpContext->pNpScb == pNpScb ); + + // + // If we are cleaning up the last vcb on an NDS server and + // there are no open streams, we can unlicense the connection. + // + + if ( ( pScb->MajorVersion > 3 ) && + ( pScb->UserName.Length == 0 ) && + ( pScb->VcbCount == 0 ) && + ( pScb->OpenNdsStreams == 0 ) ) { + NdsUnlicenseConnection( IrpContext ); + } + + // + // If this is a VCB for a share, remove the volume handle. + // + + if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + + Handle = pVcb->Specific.Disk.Handle; + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "Sb", + NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE, + Handle ); + + if ( NT_SUCCESS( Status )) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N" ); + } + } + + // + // We can now free the VCB memory. + // + + FREE_POOL( pVcb ); + + // + // If there are no handles open (and hence no explicit connections) + // and this is a bindery login, then we should logout and disconnect + // from this server. This is most important when a user has a + // login count on a server set to 1 and wants to access the server + // from another machine. + // + // Release the RCB in case we get off the head of the queue in + // NwLogoffAndDisconnect. + // + + NwReleaseRcb( &NwRcb ); + + if ( ( pScb->IcbCount == 0 ) && + ( pScb->OpenFileCount == 0 ) && + ( pNpScb->State == SCB_STATE_IN_USE ) && + ( pScb->UserName.Length != 0 ) ) { + + NwLogoffAndDisconnect( IrpContext, pNpScb ); + } + + // + // We might need to restore the server pointers. + // + + NwDequeueIrpContext( IrpContext, FALSE ); + NwDereferenceScb( pScb->pNpScb ); + + DebugTrace(-1, Dbg, "NwCleanupVcb exit\n", 0); + return; +} + +VOID +NwCloseAllVcbs( + PIRP_CONTEXT pIrpContext + ) +/*++ + +Routine Description: + + This routine sends closes all open VCB handles. + +Arguments: + + pIrpContext - The IRP context for this request. + +Return Value: + + none. + +--*/ +{ + KIRQL OldIrql; + PLIST_ENTRY ScbQueueEntry, NextScbQueueEntry; + PLIST_ENTRY VcbQueueEntry, NextVcbQueueEntry; + PNONPAGED_SCB pNpScb; + PSCB pScb; + PVCB pVcb; + BOOLEAN VcbDeleted; + + PAGED_CODE(); + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + + for (ScbQueueEntry = ScbQueue.Flink ; + ScbQueueEntry != &ScbQueue ; + ScbQueueEntry = NextScbQueueEntry ) { + + pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks ); + NextScbQueueEntry = pNpScb->ScbLinks.Flink; + + pScb = pNpScb->pScb; + if ( pScb == NULL ) { + continue; + } + + NwReferenceScb( pNpScb ); + KeReleaseSpinLock( &ScbSpinLock, OldIrql ); + + // + // Get to the head of the SCB queue so that we don't deadlock + // if we need to send packets in NwCleanupVcb(). + // + + pIrpContext->pNpScb = pNpScb; + pIrpContext->pScb = pNpScb->pScb; + + NwAppendToQueueAndWait( pIrpContext ); + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + // + // NwCleanupVcb releases the RCB, but we can't be guaranteed + // the state of the Vcb list when we release the RCB. + // + // If we need to cleanup a VCB, release the lock, and start + // processing the list again. + // + + VcbDeleted = TRUE; + + while ( VcbDeleted ) { + + VcbDeleted = FALSE; + + // + // Walk the list of VCBs for this SCB + // + + for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink; + VcbQueueEntry != &pScb->ScbSpecificVcbQueue; + VcbQueueEntry = NextVcbQueueEntry ) { + + pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry ); + NextVcbQueueEntry = VcbQueueEntry->Flink; + + // + // If this VCB is mapped to a drive letter, delete the mapping + // now. + // + + if ( BooleanFlagOn( pVcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) { + + // + // Remove the VCB from the global list. + // + + ClearFlag( pVcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ); + --pVcb->Reference; + --pVcb->Scb->OpenFileCount; + } + + if ( pVcb->DriveLetter >= L'A' && pVcb->DriveLetter <= L'Z' ) { + DriveMapTable[ pVcb->DriveLetter - 'A' ] = NULL; + } else if ( pVcb->DriveLetter >= L'1' && pVcb->DriveLetter <= L'9' ) { + DriveMapTable[ MAX_DISK_REDIRECTIONS + pVcb->DriveLetter - '1' ] = NULL; + } else { + ASSERT( pVcb->DriveLetter == 0 ); + } + + if ( pVcb->Reference == 0 ) { + + NwCleanupVcb( pVcb, pIrpContext ); + + // + // Get back to the head of the queue. + // + + NwAppendToQueueAndWait( pIrpContext ); + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + VcbDeleted = TRUE; + break; + + } else { + SetFlag( pVcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY ); + } + + } + } + + // + // Get off the head of this SCB and move on. + // + + KeAcquireSpinLock( &ScbSpinLock, &OldIrql ); + NwDequeueIrpContext( pIrpContext, TRUE ); + NwReleaseRcb( &NwRcb ); + NwDereferenceScb( pNpScb ); + } + + KeReleaseSpinLock( &ScbSpinLock, OldIrql ); + +} + +BOOLEAN +GetLongNameSpaceForVolume( + IN PIRP_CONTEXT IrpContext, + IN UNICODE_STRING ShareName, + OUT PCHAR VolumeLongNameSpace, + OUT PCHAR VolumeNumber + ) +/*++ + +Routine Description: + + This routine determines the name space index for long name support. + This is accomplished by looking for the OS2 name space. + +Arguments: + + pIrpContext - The IRP context for this request. + + ShareName - The name of the interesting volume. + + VolumeLongNameSpace - Returns the name space id of the OS/2 name space. + + VolumeNumber - Returns the volume number. + +Return Value: + + TRUE - The volume support long names. + FALSE - The volume does not support long names. + +--*/ +{ + NTSTATUS Status; + char *ptr; + int i; + char length; + BOOLEAN LongNameSpace; + CHAR NumberOfNameSpaces, NumberOfInfoRecords; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "GetLongNameSpaceForVolume...\n", 0); + + *VolumeLongNameSpace = LFN_NO_OS2_NAME_SPACE; + + // + // Get the ordinal number of this volume. + // + + for ( i = 0; ShareName.Buffer[i] != ':'; i++); + ShareName.Length = i * sizeof( WCHAR ); + + DebugTrace( 0, Dbg, "Volume name %wZ\n", &ShareName ); + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "SU", + NCP_DIR_FUNCTION, NCP_GET_VOLUME_NUMBER, + &ShareName ); + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nb", + VolumeNumber ); + } + + if ( !NT_SUCCESS( Status )) { + DebugTrace( 0, Dbg, "Couldn't get volume number\n", 0); + DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> -1\n", 0); + return( FALSE ); + } + + // + // Send a get name space info request, and wait for the response. + // + + DebugTrace( 0, Dbg, "Querying volume number %d\n", *VolumeNumber ); + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "Sb", + NCP_DIR_FUNCTION, NCP_GET_NAME_SPACE_INFO, + *VolumeNumber ); + + if ( NT_SUCCESS( Status )) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nb", + &NumberOfNameSpaces ); + } + + if ( !NT_SUCCESS( Status )) { + DebugTrace( 0, Dbg, "Couldn't get name space info\n", 0); + DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> -1\n", 0); + return( FALSE ); + } + + // + // Parse the response, it has the following format: + // + // NCP Header + // + // Number of Name Space Records (n1, byte) + // + // n1 Name Space Records + // Length (l1, byte) + // Value (l1 bytes, non-NUL-terminated ASCII string) + // + // Number of Name Space Info Records (n2, byte) + // + // n2 Name Space Info Records + // Record number (byte) + // Length (l2, byte) + // Value (l2 bytes, non-NUL-terminated ASCII string) + // + // Loaded name spaces (n3, byte) + // Loaded name space list (n3 bytes, each byte refers to the ordinal + // number of a name space record ) + // + // Volume name spaces (n3, byte) + // Volume name space list (n3 bytes, as above) + // + // Volume Data Streams (n3, byte) + // Volume Data Streams (n3 bytes, each byte refers to the ordinal + // number of a name space info record ) + // + + DebugTrace( 0, Dbg, "Number of name spaces = %d\n", NumberOfNameSpaces ); + + ptr = &IrpContext->rsp[ 9 ]; + LongNameSpace = FALSE; + + // + // Skip the loaded name space list. + // + + for ( i = 0 ; i < NumberOfNameSpaces ; i++ ) { + length = *ptr++; + ptr += length; + } + + // + // Skip the supported data streams list. + // + + NumberOfInfoRecords = *ptr++; + + for ( i = 0 ; i < NumberOfInfoRecords ; i++ ) { + ptr++; // Skip record number + length = *ptr; + ptr += length + 1; + } + + // + // Skip the supported data streams ordinal list. + // + + length = *ptr; + ptr += length + 1; + + // + // See if this volume supports long names. + // + + length = *ptr++; + for ( i = 0; i < length ; i++ ) { + if ( *ptr++ == LONG_NAME_SPACE_ORDINAL ) { + LongNameSpace = TRUE; + *VolumeLongNameSpace = LONG_NAME_SPACE_ORDINAL; + } + } + + if ( LongNameSpace ) { + DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> STATUS_SUCCESS\n", 0 ); + } else { + DebugTrace(-1, Dbg, "No long name space for volume.\n", 0 ); + } + + return( LongNameSpace ); +} + +BOOLEAN +IsFatNameValid ( + IN PUNICODE_STRING FileName + ) +/*++ + +Routine Description: + + This routine checks if the specified file name is conformant to the + Fat 8.3 file naming rules. + + FIXFIX Either get a wide version of FsRtlIsFatDbcsLegal or keep the OemString + FIXFIX version around for the packet we'll eventually create. + +Arguments: + + FileName - Supplies the name to check. + +Return Value: + + BOOLEAN - TRUE if the name is valid, FALSE otherwise. + +--*/ + +{ + STRING DbcsName; + int i; + + PAGED_CODE(); + + // + // Build up the dbcs string to call the fsrtl routine to check + // for legal 8.3 formation + // + + if (NT_SUCCESS(RtlUnicodeStringToCountedOemString( &DbcsName, FileName, TRUE))) { + + for ( i = 0; i < DbcsName.Length; i++ ) { + + if ( FsRtlIsLeadDbcsCharacter( DbcsName.Buffer[i] ) ) { + + // + // Ignore lead bytes and trailing bytes + // + + i++; + + } else { + + // + // disallow: + // '*' + 0x80 alt-170 (0xAA) + // '.' + 0x80 alt-174 (0xAE), + // '?' + 0x80 alt-191 (0xBF) the same as Dos clients. + // + // May need to add 229(0xE5) too. + // + // We also disallow spaces as valid FAT chars since + // NetWare treats them as part of the OS2 name space. + // + + if ((DbcsName.Buffer[i] == 0xAA) || + (DbcsName.Buffer[i] == 0xAE) || + (DbcsName.Buffer[i] == 0xBF) || + (DbcsName.Buffer[i] == ' ')) { + + RtlFreeOemString( &DbcsName ); + return FALSE; + } + } + } + + if (FsRtlIsFatDbcsLegal( DbcsName, FALSE, TRUE, TRUE )) { + + RtlFreeOemString( &DbcsName ); + + return TRUE; + + } + + RtlFreeOemString( &DbcsName ); + } + + // + // And return to our caller + // + + return FALSE; +} + +CHAR +GetNewDriveNumber ( + IN PSCB Scb + ) +/*++ + +Routine Description: + + Portable NetWare needs us to give a different drive letter each time + we ask for a permanent handle. If we use the same one then: + + net use s: \\port\sys + net use v: \\port\vol1 + dir s: + <get contents of \\port\vol1 !!!!> + + +Arguments: + + Scb + +Return Value: + + Letter assigned. + +--*/ + +{ + + ULONG result = RtlFindClearBitsAndSet( &Scb->DriveMapHeader, 1, 0 ); + + PAGED_CODE(); + + if (result == 0xffffffff) { + return(0); // All used! + } else { + return('A' + (CHAR)(result & 0x00ff) ); + } +} + +VOID +FreeDriveNumber( + IN PSCB Scb, + IN CHAR DriveNumber + ) +/*++ + +Routine Description: + + This routine releases the appropriate Drivehandles bit. + +Arguments: + + FileName - Supplies the name to check. + +Return Value: + + BOOLEAN - TRUE if the name is valid, FALSE otherwise. + +--*/ + +{ + PAGED_CODE(); + + if (DriveNumber) { + RtlClearBits( &Scb->DriveMapHeader, (DriveNumber - 'A') & 0x00ff, 1); + } +} |