You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3289 lines
86 KiB
3289 lines
86 KiB
/*++
|
|
|
|
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 )
|
|
#pragma alloc_text( PAGE, NwFreeDirCacheForIcb )
|
|
|
|
#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_PTR)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.
|
|
//
|
|
|
|
ExInitializeResourceLite( &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_PTR)Rcb);
|
|
|
|
//
|
|
// Uninitialize the resource variable for the RCB.
|
|
//
|
|
|
|
ExDeleteResourceLite( &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 );
|
|
|
|
}
|
|
|
|
InitializeListHead( &(Icb->DirCache) );
|
|
|
|
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_PTR)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 );
|
|
|
|
}
|
|
|
|
NwFreeDirCacheForIcb( Icb );
|
|
|
|
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.
|
|
//
|
|
|
|
ExInitializeResourceLite( &NpFcb->Resource );
|
|
|
|
//
|
|
// Initialize Advanced FCB Header fields
|
|
//
|
|
|
|
ExInitializeFastMutex( &NpFcb->AdvancedFcbHeaderMutex );
|
|
FsRtlSetupAdvancedHeader( &NpFcb->Header,
|
|
&NpFcb->AdvancedFcbHeaderMutex );
|
|
|
|
//
|
|
// Copy the file name
|
|
//
|
|
|
|
RtlCopyMemory( FileNameBuffer, FileName->Buffer, FileName->Length );
|
|
Fcb->FullFileName.MaximumLength = FileName->Length;
|
|
Fcb->FullFileName.Length = FileName->Length;
|
|
Fcb->FullFileName.Buffer = FileNameBuffer;
|
|
|
|
// Mapping for Novell's handling of Euro char in file names
|
|
{
|
|
int i = 0;
|
|
WCHAR * pCurrChar = FileNameBuffer;
|
|
for (i = 0; i < (FileName->Length / 2); i++)
|
|
{
|
|
if (*(pCurrChar + i) == (WCHAR) 0x20AC) // Its a Euro
|
|
*(pCurrChar + i) = (WCHAR) 0x2560; // set it to Novell's mapping for Euro
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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, ensuring we don't cause overflow
|
|
//
|
|
|
|
if ((ULONG)(Dcb->FullFileName.Length + FileName->Length) > (0xFFFF - 2)) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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 );
|
|
}
|
|
|
|
//
|
|
// Release any Filter Context structures associated with this FCB
|
|
//
|
|
|
|
FsRtlTeardownPerStreamContexts( &NpFcb->Header );
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
ExDeleteResourceLite( &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' ) {
|
|
PVCB * DriveMapTable = GetDriveMapTable( IrpContext->Specific.Create.UserUid );
|
|
Vcb = DriveMapTable[DriveLetter - L'A'];
|
|
} else {
|
|
PVCB * DriveMapTable = GetDriveMapTable( IrpContext->Specific.Create.UserUid );
|
|
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 );
|
|
|
|
//
|
|
// TRACKING: If this is the deref of a directory map, the path we have
|
|
// been provided may be the short name space path. We don't know
|
|
// how to get the long name path to connect up the long name space
|
|
// for the user, which could cause problems...
|
|
//
|
|
|
|
if ( ( IrpContext->Specific.Create.NdsCreate ) &&
|
|
( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_DIRMAP ) ) {
|
|
|
|
if ( ( LongNameSpace == LONG_NAME_SPACE_ORDINAL ) &&
|
|
( IsFatNameValid( &LongShareName ) ) &&
|
|
( !FavourLongNames ) ) {
|
|
|
|
LongNameSpace = LFN_NO_OS2_NAME_SPACE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check to see if long names have been completely
|
|
// disabled in the registry...
|
|
//
|
|
|
|
if ( LongNameFlags & LFN_FLAG_DISABLE_LONG_NAMES ) {
|
|
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 = ExchangeWithWait(
|
|
IrpContext,
|
|
SynchronousResponseCallback,
|
|
"SdwJ", // Format string
|
|
NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
|
|
-1, // Previous ID
|
|
OT_JOBQUEUE,
|
|
&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' ) {
|
|
PVCB * DriveMapTable = GetDriveMapTable( Scb->UserUid );
|
|
DriveMapTable[DriveLetter - 'A'] = Vcb;
|
|
} else {
|
|
PVCB * DriveMapTable = GetDriveMapTable( Scb->UserUid );
|
|
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;
|
|
}
|
|
|
|
//
|
|
// tommye - MS bug 71690- Calculate the path
|
|
//
|
|
|
|
if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
|
|
Vcb->Path.Buffer = Vcb->Name.Buffer + 3;
|
|
Vcb->Path.Length = Vcb->Name.Length - 6;
|
|
} else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
|
|
Vcb->Path.Buffer = Vcb->Name.Buffer + 5;
|
|
Vcb->Path.Length = Vcb->Name.Length - 10;
|
|
} else {
|
|
Vcb->Path = Vcb->Name;
|
|
}
|
|
|
|
// Strip off the unicode prefix
|
|
|
|
Vcb->Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
|
|
Vcb->Path.Length -= Vcb->Scb->UnicodeUid.Length;
|
|
Vcb->Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
|
|
|
|
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 ) {
|
|
PVCB * DriveMapTable = GetDriveMapTable( pScb->UserUid );
|
|
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 );
|
|
|
|
ScbQueueEntry = ScbQueue.Flink;
|
|
|
|
if (ScbQueueEntry != &ScbQueue) {
|
|
pNpScb = CONTAINING_RECORD(ScbQueueEntry,
|
|
NONPAGED_SCB,
|
|
ScbLinks);
|
|
NwReferenceScb( pNpScb );
|
|
}
|
|
|
|
for (;
|
|
ScbQueueEntry != &ScbQueue ;
|
|
ScbQueueEntry = NextScbQueueEntry ) {
|
|
|
|
pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
|
|
NextScbQueueEntry = pNpScb->ScbLinks.Flink;
|
|
|
|
|
|
//
|
|
// Reference the next entry in the list before letting go of the ScbSpinLock
|
|
// to ensure that the scavenger doesn't destroy it.
|
|
//
|
|
|
|
if (NextScbQueueEntry != &ScbQueue) {
|
|
PNONPAGED_SCB pNextNpScb = CONTAINING_RECORD(NextScbQueueEntry,
|
|
NONPAGED_SCB,
|
|
ScbLinks);
|
|
|
|
NwReferenceScb( pNextNpScb );
|
|
}
|
|
|
|
pScb = pNpScb->pScb;
|
|
|
|
if ( pScb == NULL ) {
|
|
NwDereferenceScb( pNpScb );
|
|
continue;
|
|
}
|
|
|
|
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' ) {
|
|
PVCB * DriveMapTable = GetDriveMapTable( pScb->UserUid );
|
|
DriveMapTable[ pVcb->DriveLetter - 'A' ] = NULL;
|
|
} else if ( pVcb->DriveLetter >= L'1' && pVcb->DriveLetter <= L'9' ) {
|
|
PVCB * DriveMapTable = GetDriveMapTable( pScb->UserUid );
|
|
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;
|
|
USHORT 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.
|
|
|
|
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] ) ) {
|
|
|
|
if (Korean){
|
|
//
|
|
// Korean NT supports a large DBCS code-range than Korean
|
|
// Netware. We block the extra code-range to avoid
|
|
// code conversion problems.
|
|
//
|
|
if ( (UCHAR) DbcsName.Buffer[i] >=0x81 && (UCHAR) DbcsName.Buffer[i] <=0xA0){
|
|
RtlFreeOemString( &DbcsName );
|
|
return FALSE;
|
|
}else if((UCHAR) DbcsName.Buffer[i+1] <=0xA0){
|
|
RtlFreeOemString( &DbcsName );
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
NwFreeDirCacheForIcb(
|
|
IN PICB Icb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees the directory cache associated with an ICB.
|
|
|
|
Arguments:
|
|
|
|
Icb - Supplies the ICB to clear the dir cache on.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
Icb->CacheHint = NULL;
|
|
|
|
InitializeListHead( &(Icb->DirCache) );
|
|
|
|
if( Icb->DirCacheBuffer ) {
|
|
FREE_POOL( Icb->DirCacheBuffer );
|
|
}
|
|
|
|
Icb->DirCacheBuffer = NULL;
|
|
}
|
|
|