windows-nt/Source/XPSP1/NT/base/fs/mup/dnr.c
2020-09-26 16:20:57 +08:00

5168 lines
163 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+-------------------------------------------------------------------------
//
// Copyright (C) 1992, Microsoft Corporation.
//
// File: dnr.c
//
// Contents: Distributed name resolution process and control
//
// Functions: DnrStartNameResolution -- Start a name resolution
// DnrNameResolve -- Main loop for DNR
// DnrComposeFileName -- Canonicalize file name
// DnrCaptureCredentials -- Capture user-defined creds for Dnr
// DnrReleaseCredentials -- Dual of DnrCaptureCredentials
// DnrRedirectFileOpen -- Redirect a create IRP to some provider
// DnrPostProcessFileOpen -- Resume after return from redirect
// DnrGetAuthenticatedConnection -- Using Dnr credentials
// DnrReleaseAuthenticatedConnection -- returned by above func
// DfsBuildConnectionRequest -- Builds name of server IPC$ share
// DfsFreeConnectionRequest -- Free resources allocated above
// DfsCreateConnection -- Create a connection to a server IPC$
// DfsCloseConnection -- Close connection opened above
// DnrBuildReferralRequest -- Build Irp for referral request
// DnrInsertReferralAndResume -- Resume DNR after referral
// DnrCompleteReferral -- DPC to process a referral response
// DnrCompleteFileOpen -- DPC to process a file open completion
// DnrBuildFsControlRequest -- Create an IRP for an Fsctrl
// AllocateDnrContext -- Allocate a context record for DNR
// DeallocateDnrContext -- Free context record
// DnrConcatenateFilePath -- Construct path with backslashes etc
// DnrLocateDC -- Locate the server for a Dfs root
//
//--------------------------------------------------------------------------
#include "dfsprocs.h"
#include <smbtypes.h>
#include <smbtrans.h>
#include "fsctrl.h"
#include "fcbsup.h"
#include "dnr.h"
#include "creds.h"
#include "know.h"
#include "mupwml.h"
#include <netevent.h>
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_DNR)
//
// Local function prototypes
//
#define DNR_SET_TARGET_INFO(_DnrC, _Entry) \
if (((_DnrC)->pDfsTargetInfo == NULL) && (_Entry != NULL)) {\
(_DnrC)->pDfsTargetInfo = (_Entry)->pDfsTargetInfo; \
if ((_DnrC)->pDfsTargetInfo != NULL) { \
PktAcquireTargetInfo( (_DnrC)->pDfsTargetInfo); \
} \
}
PDNR_CONTEXT
AllocateDnrContext(
IN ULONG cbExtra
);
#define DeallocateDnrContext(pNRC) ExFreePool(pNRC);
VOID
DnrRebuildDnrContext(
IN PDNR_CONTEXT DnrContext,
IN PUNICODE_STRING NewDfsPrefix,
IN PUNICODE_STRING RemainingPath);
VOID
DnrCaptureCredentials(
IN PDNR_CONTEXT DnrContext);
VOID
DnrReleaseCredentials(
IN PDNR_CONTEXT DnrContext);
NTSTATUS
DnrGetAuthenticatedConnection(
IN OUT PDNR_CONTEXT DnrContext);
VOID
DnrReleaseAuthenticatedConnection(
IN OUT PDNR_CONTEXT DnrContext);
NTSTATUS
DfsBuildConnectionRequest(
IN PDFS_SERVICE pService,
IN PPROVIDER_DEF pProvider,
OUT PUNICODE_STRING pShareName);
VOID
DfsFreeConnectionRequest(
IN OUT PUNICODE_STRING pShareName);
NTSTATUS
DnrRedirectFileOpen (
IN PDNR_CONTEXT DnrContext
);
NTSTATUS
DnrPostProcessFileOpen(
IN PDNR_CONTEXT DnrContext
);
VOID
DnrInsertReferralAndResume(
IN PVOID Context);
VOID
DnrLocateDC(
IN PUNICODE_STRING FileName);
NTSTATUS
DnrCompleteReferral(
IN PDEVICE_OBJECT pDevice,
IN PIRP Irp,
IN PVOID Context
);
NTSTATUS
DnrCompleteFileOpen(
IN PDEVICE_OBJECT pDevice,
IN PIRP Irp,
IN PVOID Context
);
PIRP
DnrBuildReferralRequest(
IN PDNR_CONTEXT pDnrContext
);
VOID
PktFlushChildren(
PDFS_PKT_ENTRY pEntry
);
VOID
MupInvalidatePrefixTable(
VOID
);
NTSTATUS
DnrGetTargetInfo(
PDNR_CONTEXT pDnrContext);
#define DFS_REFERENCE_OBJECT(d) \
ObReferenceObjectByPointer(d,0,NULL,KernelMode);
#define DFS_DEREFERENCE_OBJECT(d) \
ObDereferenceObject((PVOID)(d));
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, DnrStartNameResolution )
#pragma alloc_text( PAGE, DnrNameResolve )
#pragma alloc_text( PAGE, DnrComposeFileName )
#pragma alloc_text( PAGE, DnrCaptureCredentials )
#pragma alloc_text( PAGE, DnrReleaseCredentials )
#pragma alloc_text( PAGE, DnrGetAuthenticatedConnection )
#pragma alloc_text( PAGE, DnrReleaseAuthenticatedConnection )
#pragma alloc_text( PAGE, DnrRedirectFileOpen )
#pragma alloc_text( PAGE, DnrPostProcessFileOpen )
#pragma alloc_text( PAGE, DfsBuildConnectionRequest )
#pragma alloc_text( PAGE, DfsFreeConnectionRequest )
#pragma alloc_text( PAGE, DnrBuildReferralRequest )
#pragma alloc_text( PAGE, DfsCreateConnection )
#pragma alloc_text( PAGE, DfsCloseConnection )
#pragma alloc_text( PAGE, DnrBuildFsControlRequest )
#pragma alloc_text( PAGE, DnrInsertReferralAndResume )
#pragma alloc_text( PAGE, DnrLocateDC )
#pragma alloc_text( PAGE, AllocateDnrContext )
#pragma alloc_text( PAGE, DnrRebuildDnrContext )
#pragma alloc_text( PAGE, DnrConcatenateFilePath )
#pragma alloc_text( PAGE, DfspGetOfflineEntry)
#pragma alloc_text( PAGE, DfspMarkServerOnline)
#pragma alloc_text( PAGE, DfspMarkServerOffline)
#pragma alloc_text( PAGE, DfspIsRootOnline)
//
// The following are not pageable since they can be called at DPC level
//
// DnrCompleteReferral
// DnrCompleteFileOpen
//
#endif
//+-------------------------------------------------------------------
//
// Function: DfsIsShareNull
//
// Synopsis: Is this name of the form "\server\"?
//
// Arguments: [FileName] - pointer to the UNICODE_STRING we are checking
//
// Returns: TRUE if FileName is "\server\", FALSE otherwise
//
//--------------------------------------------------------------------
BOOLEAN
DfsIsShareNull(PUNICODE_STRING FileName)
{
USHORT RootEnd = 0;
USHORT ShareEnd = 0;
BOOLEAN result = FALSE;
USHORT Length = 0;
// find the first '\'
// we start at 1 because the first character is also '\'
for (RootEnd = 1;
RootEnd < FileName->Length/sizeof(WCHAR) &&
FileName->Buffer[RootEnd] != UNICODE_PATH_SEP;
RootEnd++) {
NOTHING;
}
// now FileName->Buffer[RootEnd] == '\' and we are beyond the root part of the name
// find the end of the share part.
for (ShareEnd = RootEnd+1;
ShareEnd < FileName->Length/sizeof(WCHAR) &&
FileName->Buffer[ShareEnd] != UNICODE_PATH_SEP;
ShareEnd++) {
NOTHING;
}
// the length of the share name is ShareEnd - RootEnd - 1
// the -1 is becasue we have actually stepped one char beyond the
// share name's end
// For example: \root\share\link RootEnd=5, ShareEnd=11
// \root\share RootEnd=5, ShareEnd=11
// \root\ RootEnd=5, ShareEnd=6
Length = (USHORT) (ShareEnd - RootEnd - 1) * sizeof(WCHAR);
if(Length == 0) {
result = TRUE;
} else {
result = FALSE;
}
return result;
}
NTSTATUS
DfsRerouteOpenToMup(
IN PFILE_OBJECT FileObject,
IN PUNICODE_STRING FileName)
{
UNICODE_STRING NewName;
ULONG nameLength;
NTSTATUS status;
nameLength = sizeof(DD_MUP_DEVICE_NAME ) + FileName->Length + sizeof(WCHAR);
if (nameLength > MAXUSHORT) {
status = STATUS_NAME_TOO_LONG;
MUP_TRACE_HIGH(ERROR, DfsRerouteOpenToMup_Error_NameTooLong,
LOGSTATUS(status)
LOGPTR(FileObject));
return status;
}
NewName.Buffer = ExAllocatePoolWithTag(
PagedPool,
nameLength,
' puM');
if ( NewName.Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
NewName.MaximumLength = (USHORT)nameLength;
NewName.Length = 0;
RtlAppendUnicodeToString(&NewName, DD_MUP_DEVICE_NAME);
RtlAppendUnicodeStringToString(&NewName, FileName);
if (MupVerbose) {
DbgPrint("Newname %wZ\n", &NewName);
}
ExFreePool(FileObject->FileName.Buffer);
FileObject->FileName = NewName;
return STATUS_REPARSE;
}
// The name resolution process operates as a state machine in
// which the current step in the process is indicated by a state
// variable, and responses to requests from the network will
// transition the process to other states, from which actions
// are taken.
//
// When a user request needs further processing, an IRP is
// dispatched with a Completion Routine that will
// pick up processing when the sub-request is completed. The
// completion routine will adjust the name resolution state and restart the
// main loop of the state machine.
//
// The following state/action table describes the actions of
// the procedures which implement the state machine:
//
// Current Condition/ Next
// State Action State
// ------- ---------- -----
//
// Enter Acquire Pkt, canonicalize file LocalCompletion
// name, optimistic allocation of
// FCB fails/
// No action
//
// Enter Acquire Pkt, canonicalize file Start
// name, allocated FCB/
// Capture Credentials to use
//
// Start Got a referral, new pkt entry GetFirstReplica
// is already in DnrContext and
// pkt entry is not inter-dfs/
// Capture USN of pkt entry
//
// Start lookup in PKT returns match GetFirstReplica
// and pkt entry is not inter-dfs/
// Capture USN of pkt entry
//
// Start pkt entry from referral or Start
// lookup is inter-dfs/
// Change file name in DnrContext
// to name in new Dfs, rebuild
// DnrContext
//
// Start lookup in PKT, no match/ GetFirstDC
// No action
//
// GetFirstReplica Find First replica fails and Done
// we have already got a referral/
// Set final status to
// NO_SUCH_DEVICE (must be
// because we don't have an
// appropriate redirector)
//
// GetFirstReplica Find First replica fails and GetFirstDC
// we haven't yet got a referral/
// locate first DC to send
// referral request to.
//
// GetFirstReplica Replica found has no address, GetFirstDC
// means a domain-based Dfs with
// no DCs/
// No action
//
// GetFirstReplica Replica found with valid SendRequest
// address/
// Capture provider info under
// lock protection, Reference
// provider's device object,
//
// SendRequest Supplied credentials, and tree Done
// connect using creds fails/
// Set final status, dereference
// provider's device object
//
// SendRequest Allocate pool for new name/ PostProcessOpen
// Change file name into one that
// the provider can parse, pass
// the Create request to the
// provider, Derefence provider's
// device object when provider
// completes the request.
//
// SendRequest Pool Allocation fails/ Done
// Set final status, dereference
// provider's device object
//
// PostProcessOpen Underlying FS returned REPARSE, SendRequest
// successfully created or found a
// provider for the target redir/
// Capture provider information
// under lock protection,
// Reference providers Device obj.
//
// PostProcessOpen Underlying FS returned SUCCESS/ Done
// Insert optimistically allocated
// FCB into Fcb table, set final
// status
//
// PostProcessOpen Open failed with GetFirstDC
// PATH_NOT_COVERED or
// DFS_EXIT_POINT_FOUND, and
// we haven't yet gotten a
// referral/
// No action
//
// PostProcessOpen Open failed with Start
// OBJECT_TYPE_MISMATCH (ie,
// downlevel open found an
// interdfs link)/Change
// name in DnrContext to name
// in new Dfs, rebuild DnrContext
//
// PostProcessOpen Open failed with GetFirstReplica
// PATH_NOT_COVERED or
// DFS_EXIT_POINT_FOUND, and we
// already got a referral, and
// we have never reported an
// inconsistency/
// Report inconsistency
//
// PostProcessOpen Same as above, but we already GetNextReplica
// reported the inconsistency/
// Report the inconsistency
//
// PostProcessOpen Open failed with network error/ GetNextReplica
// No action
//
// PostProcessOpen Open failed with non-network Done
// error/
// Set final status
//
// GetNextReplica No more replicas and haven't GetFirstDC
// gotten a referral yet/
// no action
//
// GetNextReplica No more replicas and got a Done
// referral/
// no action
//
// GetNextReplica Replica found/ SendRequest
// Capture provider information
// under lock protection,
// Reference provider Device obj
//
// GetFirstDC Lookup referral entry not Done
// found or has no services, and
// we have already called DC
// Locator once/
// Set final status to
// CANT_ACCESS_DOMAIN_INFO
//
// GetFirstDC Lookup referral entry returned Done
// valid entry, but can't find a
// provider for it/
// Set final status to
// CANT_ACCESS_DOMAIN_INFO
//
// GetFirstDC Lookup referral entry returned GetReferrals
// valid entry, and found
// provider/
// Set DnrContext->pPktEntry to
// DC's entry, Capture provider
// info under lock protection,
// Reference provider's Device obj
//
// GetReferrals Unable to open DC's IPC$ share/ GetNextDC
// Dereference provider's device
// object
//
// GetReferrals Opened DC's IPC$ share, but Done
// unable to build referral
// request Irp/
// Dereference provider's device,
// Set final status to
// INSUFFICIENT_RESOURCES
//
// GetReferrals Opened DC's IPC$ share and CompleteReferral
// built referral request/
// Release Pkt, Send referral
// request
//
// GetNextDC Successfully found another GetReferrals
// DC/
// Capture provider info under
// lock protection, Reference
// provider's Device object
//
// GetNextDC Can't find another DC/ Done
// Set final status to
// CANT_ACCESS_DOMAIN_INFO
//
// Done Complete create Irp with
// DnrContext->FinalStatus
//
// LocalCompletion Complete create Irp with
// local status.
//
// CompleteReferral Referral returned with GetReferrals
// BUFFER_OVERFLOW/
// Set referral size to
// indicated amount
//
// CompleteReferral Referral returned, but Done
// error in creating entry/
// Dereference provider's
// device, set final status to
// result of creating entry
//
// CompleteReferral Referral returned and GetFirstDC
// successfully created entry,
// entry is inter-dfs/
// Dereference provider's device,
// Reset ReferralSize
//
// CompleteReferral Same as above, but entry Start
// is storage entry/
// Dereference provider's device,
// Adjust DnrContext->
// RemainingPart to correspond
// to the new entry
//
// CompleteReferral Referral request failed with GetNextDC
// some network error/
// Dereference Provider's device
//
// CompleteReferral Referral request failed with Done
// some non-network error/
// Dereference provider's Device,
// Set final status to this error
//
//+-------------------------------------------------------------------------
//
// Function: DnrStartNameResolution - Start a distributed name resolution
//
// Synopsis: DnrStartNameResolution starts the name resolution process
// for a request (typically an NtCreateFile).
//
// Effects: Could change the state of the PKT or individual
// PKT entries.
//
// Arguments: [IrpContext] - pointer to a IRP_CONTEXT structure for the
// current request.
// [Irp] - IRP being processed.
// [Vcb] - Vcb of logical root.
//
// Returns: NTSTATUS - Status to be returned to the I/O subsystem.
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
DnrStartNameResolution(
IN PIRP_CONTEXT IrpContext,
IN PIRP Irp,
IN PDFS_VCB Vcb
) {
PDNR_CONTEXT DnrContext;
NTSTATUS Status;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
PUNICODE_STRING LogRootPrefix = &Vcb->LogRootPrefix;
ULONG CreateOptions;
USHORT cbFileName;
SECURITY_QUALITY_OF_SERVICE sqos;
ULONG cbFileNameLong;
MUP_TRACE_NORM(TRACE_IRP, DnrStartNameResolution_Entry,
LOGPTR(Irp)
LOGPTR(Vcb)
LOGPTR(FileObject)
LOGUSTR(FileObject->FileName)
LOGUSTR(*LogRootPrefix));
cbFileNameLong = FileObject->FileName.Length +
sizeof(UNICODE_PATH_SEP) +
LogRootPrefix->Length +
sizeof(UNICODE_NULL);
cbFileName = (USHORT)cbFileNameLong;
if( cbFileName != cbFileNameLong ) {
//
// The resulting name is too long -- we cannot deal with it
//
Status = STATUS_OBJECT_NAME_INVALID;
DfsCompleteRequest(IrpContext, Irp, Status);
DfsDbgTrace(0, Dbg, "DnrStartNameResolution: Exit ->%x\n", ULongToPtr(Status));
MUP_TRACE_HIGH(ERROR, DnrStartNameResolution_Error_NameTooLong,
LOGSTATUS(Status)
LOGPTR(FileObject)
LOGPTR(Irp));
return Status;
}
//
// Allocate the DnrContext used to resolve the name. We optimize
// allocation by allocating room for the FileName at the end of the
// DnrContext.
//
DnrContext = AllocateDnrContext(cbFileName);
if (DnrContext == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
DfsCompleteRequest(IrpContext, Irp, Status);
DfsDbgTrace(0, Dbg, "DnrStartNameResolution: Exit ->%x\n", ULongToPtr(Status));
MUP_TRACE_HIGH(ERROR, DnrStartNameResolution_Error2,
LOGSTATUS(Status)
LOGPTR(FileObject)
LOGPTR(Irp));
return Status;
}
DnrContext->FileName.Length = 0;
DnrContext->FileName.MaximumLength = cbFileName;
DnrContext->FileName.Buffer = (PWCHAR) ( (PBYTE) DnrContext + sizeof(DNR_CONTEXT) );
//
// Since FileName.Buffer has not been separately allocated, we set this
// to FALSE.
//
DnrContext->NameAllocated = FALSE;
//
// Capture the user's security token so we can later impersonate if
// needed.
//
sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sqos.ImpersonationLevel = SecurityImpersonation;
sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
sqos.EffectiveOnly = FALSE;
Status = SeCreateClientSecurity(
Irp->Tail.Overlay.Thread,
&sqos,
FALSE, // Remote Session
&DnrContext->SecurityContext); // Return context.
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrStartNameResolution_Error_SeCreateClientSecurity,
LOGSTATUS(Status)
LOGPTR(FileObject)
LOGPTR(Irp)
LOGUSTR(FileObject->FileName));
if (!NT_SUCCESS(Status)) {
DeallocateDnrContext( DnrContext );
DfsCompleteRequest(IrpContext, Irp, Status);
DfsDbgTrace(0, Dbg, "DnrStartNameResolution: Exit ->%x\n", ULongToPtr(Status));
return( Status );
}
DnrContext->Impersonate = FALSE;
ASSERT(NT_SUCCESS(Status));
//
// Initialize the rest of the DnrContext
//
DnrContext->AuthConn = NULL;
DnrContext->OriginalIrp = Irp;
DnrContext->pIrpContext = IrpContext;
DnrContext->Credentials = NULL;
DnrContext->FinalStatus = STATUS_SUCCESS;
DnrContext->FcbToUse = NULL;
DnrContext->Vcb = Vcb;
DnrContext->State = DnrStateEnter;
DnrContext->Attempts = 0;
DnrContext->DnrActive = FALSE;
DnrContext->ReleasePkt = FALSE;
DnrContext->GotReferral = FALSE;
DnrContext->FoundInconsistency = FALSE;
DnrContext->CalledDCLocator = FALSE;
DnrContext->CachedConnFile = FALSE;
DnrContext->ReferralSize = MAX_REFERRAL_LENGTH;
KeQuerySystemTime(&DnrContext->StartTime);
CreateOptions = IrpSp->Parameters.Create.Options;
//
// ... and resolve the name
//
return DnrNameResolve(DnrContext);
}
//+-------------------------------------------------------------------------
//
// Function: DnrNameResolve - Main loop for DNR
//
// Synopsis: DnrNameResolve drives the name resolution process
// for a request (typically an NtCreateFile).
//
// Effects: Could change the state of the PKT or individual
// PKT entries.
//
// Arguments: [DnrContext] - pointer to a DNR_CONTEXT structure which
// records the state of the DNR.
//
// Returns: NTSTATUS - Status to be returned to the I/O subsystem.
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
DnrNameResolve(
IN PDNR_CONTEXT DnrContext
) {
PIO_STACK_LOCATION IrpSp;
NTSTATUS Status = STATUS_SUCCESS;
PDFS_VCB Vcb;
PIRP Irp;
BOOLEAN LastEntry;
PDFS_PKT_ENTRY shortPfxMatch;
UNICODE_STRING shortRemainingPart;
LARGE_INTEGER EndTime;
PFILE_OBJECT FileObject;
DfsDbgTrace(+1, Dbg, "DnrNameResolve: Entered\n", 0);
ASSERT( !DnrContext->DnrActive && "Recursive call to Dnr!\n");
DnrContext->DnrActive = TRUE;
//
// If we need to impersonate the original caller, do so before doing
// anything else.
//
Irp = DnrContext->OriginalIrp;
IrpSp = IoGetCurrentIrpStackLocation(Irp);
FileObject = IrpSp->FileObject;
Vcb = DnrContext->Vcb;
if (DnrContext->Impersonate) {
Status = SeImpersonateClientEx(
&DnrContext->SecurityContext,
(PETHREAD) NULL);
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrNameResolve_Error_SeImpersonateClientEx,
LOGSTATUS(Status)
LOGPTR(FileObject));
if (!NT_SUCCESS(Status)) {
DnrContext->DnrActive = FALSE;
DnrContext->State = DnrStateLocalCompletion;
DfsDbgTrace(0, Dbg,
"DnrNameResolve quitting due to SeImpersonateClientEx returning 0x%x\n", ULongToPtr(Status));
}
}
//
// Drive the name resolution process as far as possible before
// it is necessary to wait for an I/O completion.
//
while (1) {
PDFS_PKT_ENTRY pktEntry = NULL;
PFILE_OBJECT FileObject = IrpSp->FileObject;
if (DnrContext->State == DnrStateGetFirstReplica ||
DnrContext->State == DnrStateGetFirstDC) {
if (++DnrContext->Attempts > MAX_DNR_ATTEMPTS) {
Status = STATUS_BAD_NETWORK_PATH;
DnrContext->State = DnrStateLocalCompletion;
DfsDbgTrace(0, 0,
"DFS: DnrNameResolve quitting due to MAX_DNR_ATTEMPTS %d\n",
UIntToPtr(DnrContext->Attempts));
}
}
if (DnrContext->State == DnrStateStart)
{
if (DnrContext->Attempts > MAX_DNR_ATTEMPTS)
{
Status = STATUS_BAD_NETWORK_PATH;
DnrContext->State = DnrStateLocalCompletion;
}
}
MUP_TRACE_LOW(DNR, DnrNameResolve_TopOfLoop,
LOGUSTR(FileObject->FileName)
LOGULONG(DnrContext->State)
);
switch (DnrContext->State) {
case DnrStateEnter:
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateEnter\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
ASSERT(DnrContext->ReleasePkt == FALSE);
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
//
// We need to construct the fully qualified file name given the
// logical root and the input file name relative to that root.
// DnrComposeFileName will allocate memory to hold a string that
// is the concatenation of the name of the logical root wrt org
// and the file name.
//
//
ASSERT((FileObject->FileName.Length & 0x1) == 0);
DnrComposeFileName(
&DnrContext->FileName,
DnrContext->Vcb,
FileObject->RelatedFileObject,
&FileObject->FileName);
DnrContext->ContextFileName = DnrContext->FileName;
DfsDbgTrace(0, Dbg,
"DnrComposeFileName -> %wZ\n", &FileObject->FileName);
if(DfsIsShareNull(&DnrContext->FileName)) {
//
// It doesn't make sense for us to have a name in the form
// "\server\" or "\domain\" so we reject it. If we didn't reject these
// names we would wind up bugchecking when processing the
// referral with a NULL sharename.
//
Status = STATUS_INVALID_PARAMETER;
DnrContext->State = DnrStateLocalCompletion;
break;
}
Status = DfspIsRootOnline(&DnrContext->FileName,
(BOOLEAN)
((DnrContext->Vcb->VcbState & VCB_STATE_CSCAGENT_VOLUME) != 0));
if (!NT_SUCCESS(Status)) {
DnrContext->State = DnrStateLocalCompletion;
break;
}
#if DBG
if (MupVerbose)
DbgPrint(" DnrContext->FileName=(%wZ)\n", &DnrContext->FileName);
#endif
//
// Allocate an FCB now for use if the DNR succeeds. We must, do
// this, or we won't know how what to do if the underlying FS
// opens the file and then we are unable to allocate the FCB.
//
ASSERT(DnrContext->FcbToUse == NULL);
DnrContext->FcbToUse = DfsCreateFcb(
NULL,
DnrContext->Vcb,
&DnrContext->ContextFileName);
if (DnrContext->FcbToUse == NULL) {
DfsDbgTrace(0, Dbg, "Could not create FCB!\n", 0);
Status = STATUS_INSUFFICIENT_RESOURCES;
DnrContext->State = DnrStateLocalCompletion;
break;
}
DnrContext->FcbToUse->FileObject = FileObject;
DfsSetFileObject(FileObject,
RedirectedFileOpen,
DnrContext->FcbToUse);
DnrCaptureCredentials(DnrContext);
DnrContext->State = DnrStateStart;
//
// Fall through
//
case DnrStateStart:
DfsDbgTrace(0, Dbg, "FSM state Start\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateStart\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
DbgPrint(" DnrContext->FileName=(%wZ)\n", &DnrContext->FileName);
}
#endif
ASSERT( PKT_LOCKED_FOR_SHARED_ACCESS() );
//
// Try to match the filename with the best
// PktEntry we have.
//
//
// Do the match in the full prefix table
//
pktEntry = PktLookupEntryByPrefix(&DfsData.Pkt,
&DnrContext->FileName,
&DnrContext->RemainingPart);
//
// Then do a match in the short prefix table
//
shortPfxMatch = PktLookupEntryByShortPrefix(
&DfsData.Pkt,
&DnrContext->FileName,
&shortRemainingPart);
if (shortPfxMatch != NULL) {
if (pktEntry == NULL) {
pktEntry = shortPfxMatch;
DnrContext->RemainingPart = shortRemainingPart;
} else if (shortPfxMatch->Id.Prefix.Length >
pktEntry->Id.Prefix.Length) {
pktEntry = shortPfxMatch;
DnrContext->RemainingPart = shortRemainingPart;
}
}
//
// If the entry found is stale and this is our first attempt at dnr,
// force another referral request.
//
if (DnrContext->Attempts == 0 && pktEntry != NULL && pktEntry->ExpireTime <= 0) {
#if DBG
if (MupVerbose)
DbgPrint(" pktEntry [%wZ] is stale - force getting another\n",
&pktEntry->Id.Prefix);
#endif
DnrContext->pPktEntry = pktEntry;
DnrContext->State = DnrStateGetFirstDC;
//
// Now break out so we restart Dnr and get a referral
//
break;
}
if (pktEntry == NULL) {
PUNICODE_STRING filePath = &DnrContext->FileName;
UNICODE_STRING dfsRootName;
UNICODE_STRING shareName;
NTSTATUS status;
PDFS_SPECIAL_ENTRY pSpecialEntry;
ULONG i, j;
for (i = 1;
i < filePath->Length/sizeof(WCHAR) &&
filePath->Buffer[i] != UNICODE_PATH_SEP;
i++) {
NOTHING;
}
dfsRootName.Length = (USHORT) ((i-1) * sizeof(WCHAR));
dfsRootName.MaximumLength = dfsRootName.Length;
dfsRootName.Buffer = &filePath->Buffer[1];
for (j = i+1;
j < filePath->Length/sizeof(WCHAR) &&
filePath->Buffer[j] != UNICODE_PATH_SEP;
j++) {
NOTHING;
}
shareName.Length = (USHORT) (j - i - 1) * sizeof(WCHAR);
shareName.MaximumLength = shareName.Length;
shareName.Buffer = &filePath->Buffer[i+1];
PktRelease();
DnrContext->ReleasePkt = FALSE;
status = PktExpandSpecialName(
&dfsRootName,
&pSpecialEntry);
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
if (NT_SUCCESS(status)) {
ULONG Len;
if ((j+1) < filePath->Length/sizeof(WCHAR)) {
Len = filePath->Length - ((j+1) * sizeof(WCHAR));
DnrContext->RemainingPart.Buffer = &filePath->Buffer[j+1];
DnrContext->RemainingPart.Length = (USHORT) Len;
DnrContext->RemainingPart.MaximumLength = (USHORT) Len;
} else {
DnrContext->RemainingPart.Buffer = NULL;
DnrContext->RemainingPart.Length = 0;
DnrContext->RemainingPart.MaximumLength = 0;
}
status = PktEntryFromSpecialEntry(
pSpecialEntry,
&shareName,
&pktEntry);
InterlockedDecrement(&pSpecialEntry->UseCount);
}
}
#if 0
if (pktEntry != NULL) {
pktEntry->ExpireTime = pktEntry->TimeToLive;
}
#endif
DfsDbgTrace(0, Dbg, "DnrNameResolve: found pktEntry %08lx\n",
pktEntry);
DNR_SET_TARGET_INFO( DnrContext, pktEntry );
if (pktEntry == NULL) {
//
// We didn't find any entry. We set pPktEntry to NULL so that
// in GetFirstDC, the call to PktLookupReferralEntry will
// return the right thing (ie, will give use the highest DC we
// know about).
//
DnrContext->pPktEntry = NULL;
DnrContext->State = DnrStateGetFirstDC;
} else if (pktEntry->Type & PKT_ENTRY_TYPE_OUTSIDE_MY_DOM) {
DnrRebuildDnrContext(
DnrContext,
&pktEntry->Info.ServiceList[0].Address,
&DnrContext->RemainingPart);
//
// The DnrContext has been rebuilt and programmed to
// "restart" DNR. So, we'll just break out of the state
// machine and reenter it with the reconstructed context
//
} else {
ASSERT(pktEntry != NULL);
DnrContext->pPktEntry = pktEntry;
DnrContext->USN = pktEntry->USN;
DnrContext->State = DnrStateGetFirstReplica;
}
break;
case DnrStateGetFirstReplica:
DfsDbgTrace(0, Dbg, "FSM state GetFirstReplica\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateGetFirstReplica\n",
(EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000));
}
#endif
ASSERT(DnrContext->ReleasePkt == TRUE);
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
Status = ReplFindFirstProvider(DnrContext->pPktEntry,
NULL,
NULL,
&DnrContext->pService,
&DnrContext->RSelectContext,
&LastEntry);
if (! NT_SUCCESS(Status)) {
ULONG PktType = DnrContext->pPktEntry->Type;
ExReleaseResourceLite(&DfsData.Resource);
DfsDbgTrace(0, Dbg, "No provider found %08lx\n", ULongToPtr(Status));
if (DnrContext->GotReferral ||
(PktType & PKT_ENTRY_TYPE_SYSVOL) != 0 ||
DnrContext->GotReparse == TRUE
) {
DnrContext->FinalStatus = STATUS_NO_SUCH_DEVICE;
DnrContext->State = DnrStateDone;
break;
} else {
DnrContext->State = DnrStateGetFirstDC;
break;
}
} else if (DnrContext->pService->Address.Length == 0) {
ExReleaseResourceLite(&DfsData.Resource);
DfsDbgTrace(0, Dbg, "Service with no address, going for referral\n", 0);
DnrContext->State = DnrStateGetFirstDC;
break;
} else {
ASSERT(DnrContext->pService != NULL);
ASSERT(DnrContext->pService->pProvider != NULL);
#if DBG
if (MupVerbose)
DbgPrint(" Alternate Name=[%wZ] Address=[%wZ]\n",
&DnrContext->pService->Name,
&DnrContext->pService->Address);
#endif
DnrContext->pProvider = DnrContext->pService->pProvider;
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
if (LastEntry == TRUE) {
DnrContext->DfsNameContext.Flags |= DFS_FLAG_LAST_ALTERNATE;
} else {
DnrContext->DfsNameContext.Flags &= ~DFS_FLAG_LAST_ALTERNATE;
}
ExReleaseResourceLite(&DfsData.Resource);
DnrContext->State = DnrStateSendRequest;
}
// FALL THROUGH ...
case DnrStateSendRequest:
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateSendRequest\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
DfsDbgTrace(0, Dbg, "FSM state SendRequest\n", 0);
ASSERT(DnrContext->ReleasePkt == TRUE);
ASSERT(DnrContext->pService != NULL);
ASSERT(DnrContext->pProvider != NULL);
ASSERT(DnrContext->TargetDevice != NULL);
//
// First of all, check to see if the volume is offline
//
if (DnrContext->pService->Type & DFS_SERVICE_TYPE_OFFLINE) {
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
DnrContext->FinalStatus = STATUS_DEVICE_OFF_LINE;
DnrContext->State = DnrStateDone;
DfsDbgTrace(-1, Dbg, "DnrNameResolve: Device Offline\n",0);
Status = STATUS_DEVICE_OFF_LINE;
MUP_TRACE_HIGH(ERROR, DnrNameResolve_Error3,
LOGSTATUS(Status)
LOGPTR(FileObject));
break;
}
//
// Next, try to make an authenticated connection to the server, if needed
//
// The pkt lock may be dropped in this call, so keep the pkt entry from going
// away.
//
InterlockedIncrement(&DnrContext->pPktEntry->UseCount);
PktRelease();
DnrContext->ReleasePkt = FALSE;
Status = DnrGetAuthenticatedConnection( DnrContext );
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
if (!NT_SUCCESS(Status)) {
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
DnrContext->FinalStatus = Status;
//
// If the error is such that we need to try another replica,
// do so here.
//
if (ReplIsRecoverableError(Status)) {
DnrContext->State = DnrStateGetNextReplica;
}
else {
DnrContext->State = DnrStateDone;
}
DfsDbgTrace(-1, Dbg,
"DnrNameResolve: Unable to get connection %08lx\n", ULongToPtr(Status));
break;
}
if (DnrContext->USN != DnrContext->pPktEntry->USN) {
//
// Dang, Pkt Entry changed when we made the
// connection. We'll have to retry.
//
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
DnrReleaseAuthenticatedConnection(DnrContext);
DnrContext->State = DnrStateStart;
DfsDbgTrace(-1, Dbg, "DnrNameResolve: USN delta - restarting DNR\n", 0);
break;
}
Status = DnrRedirectFileOpen(DnrContext);
if (Status == STATUS_PENDING) {
return(Status);
}
break;
case DnrStatePostProcessOpen:
DfsDbgTrace(0, Dbg, "FSM state PostProcessOpen\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStatePostProcessOpen\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
//
// We come to this state only after sending an open request over
// the net. We should never hold the Pkt while going over the net.
// Hence the sense of the assert below.
//
ASSERT(DnrContext->ReleasePkt == FALSE);
Status = DnrPostProcessFileOpen(DnrContext);
pktEntry = DnrContext->pPktEntry;
break;
case DnrStateGetNextReplica:
DfsDbgTrace(0, Dbg, "FSM state GetNextReplica\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrGetNextReplica\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
ASSERT(DnrContext->ReleasePkt == TRUE);
{
NTSTATUS ReplStatus;
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
ReplStatus = ReplFindNextProvider(DnrContext->pPktEntry,
&DnrContext->pService,
&DnrContext->RSelectContext,
&LastEntry);
if (ReplStatus == STATUS_NO_MORE_ENTRIES) {
ULONG PktType = DnrContext->pPktEntry->Type;
#if DBG
if (MupVerbose)
DbgPrint(" No more alternates...\n");
#endif
//
// If all failed and we are about to give up due to one
// of two reasons :
// 1. None of the Services for the PkEntry being used
// responded (either they are down or network down!).
// 2. Some or all of the Services have inconsistencies
// which we detected and informed the DC about along
// the way.
// If we did land up with case 2 then we really have to
// try and get a new referral and use that - just in
// case things have changed since then at the DC. So let
// us get into a GetReferral State and try once again.
//
ExReleaseResourceLite( &DfsData.Resource );
if (DnrContext->GotReferral ||
(PktType & PKT_ENTRY_TYPE_SYSVOL) != 0 ||
DnrContext->GotReparse == TRUE
) {
DnrContext->State = DnrStateDone;
} else {
DnrContext->State = DnrStateGetFirstDC;
}
} else if (NT_SUCCESS( ReplStatus )) {
//
// Found another replica, go back and retry.
//
ASSERT(DnrContext->pService != NULL);
ASSERT(DnrContext->pService->pProvider != NULL);
DnrContext->pProvider = DnrContext->pService->pProvider;
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
ExReleaseResourceLite(&DfsData.Resource);
DnrContext->State = DnrStateSendRequest;
#if DBG
if (MupVerbose)
DbgPrint(" Alternate Name=[%wZ] Address=[%wZ]\n",
&DnrContext->pService->Name,
&DnrContext->pService->Address);
#endif
if (LastEntry == TRUE) {
DnrContext->DfsNameContext.Flags |= DFS_FLAG_LAST_ALTERNATE;
} else {
DnrContext->DfsNameContext.Flags &= ~DFS_FLAG_LAST_ALTERNATE;
}
break;
} else {
ExReleaseResourceLite(&DfsData.Resource);
ASSERT(ReplStatus == STATUS_NO_MORE_ENTRIES);
}
}
break;
case DnrStateGetFirstDC:
DfsDbgTrace(0, Dbg, "FSM state GetFirstDC\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateGetFirstDC\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
ASSERT(DnrContext->ReleasePkt == TRUE);
{
NTSTATUS ReplStatus;
PDFS_PKT_ENTRY pPktEntryDC = NULL;
pPktEntryDC = PktLookupReferralEntry(&DfsData.Pkt, DnrContext->pPktEntry);
//
// If there is no root entry, or it is stale, or it has no
// services, then try for a new referral entry for the root
//
if (
pPktEntryDC == NULL
||
pPktEntryDC->ExpireTime <= 0
||
pPktEntryDC->Info.ServiceCount == 0
) {
if (DnrContext->CalledDCLocator) {
DnrContext->FinalStatus = STATUS_CANT_ACCESS_DOMAIN_INFO;
DnrContext->State = DnrStateDone;
break;
}
//
// We are unable to find a DC to go to for referrals.
// This can only happen if we don't have the pkt entry
// for the root of the Dfs. Try to get the root entry.
//
DfsDbgTrace(0, Dbg, "No DC info - will try locator\n", 0);
#if DBG
if (MupVerbose) {
if (pPktEntryDC != NULL && pPktEntryDC <= 0)
DbgPrint(" Entry is stale.\n");
DbgPrint(" No Root/DC info - will try locator\n");
}
#endif
PktRelease();
DnrContext->ReleasePkt = FALSE;
DnrLocateDC(&DnrContext->FileName);
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
DnrContext->CalledDCLocator = TRUE;
DnrContext->State = DnrStateStart;
break;
}
#if DBG
if (MupVerbose) {
if (DnrContext->pPktEntry != NULL)
DbgPrint(" DnrContext->pPktEntry=[%wZ]\n",
&DnrContext->pPktEntry->Id.Prefix);
else
DbgPrint(" DnrContext->pPktEntry=NULL\n");
DbgPrint(" pPktEntryDC=[%wZ]\n", &pPktEntryDC->Id.Prefix);
}
#endif
DnrContext->pPktEntry = pPktEntryDC;
DNR_SET_TARGET_INFO( DnrContext, DnrContext->pPktEntry );
DnrContext->USN = pPktEntryDC->USN;
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
ReplStatus = ReplFindFirstProvider(pPktEntryDC,
NULL,
NULL,
&DnrContext->pService,
&DnrContext->RDCSelectContext,
&LastEntry);
if (!NT_SUCCESS(ReplStatus)) {
ExReleaseResourceLite(&DfsData.Resource);
DnrContext->FinalStatus = STATUS_CANT_ACCESS_DOMAIN_INFO;
DnrContext->State = DnrStateDone;
break;
} else {
ASSERT(DnrContext->pService != NULL);
ASSERT(DnrContext->pService->pProvider != NULL);
InterlockedIncrement(&DnrContext->pPktEntry->UseCount);
DnrContext->pProvider = DnrContext->pService->pProvider;
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
ExReleaseResourceLite(&DfsData.Resource);
}
}
DnrContext->State = DnrStateGetReferrals;
/* FALL THROUGH */
case DnrStateGetReferrals:
DfsDbgTrace(0, Dbg, "FSM state GetReferrals\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateGetReferrals\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
ASSERT(DnrContext->ReleasePkt == TRUE);
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
//
// Attempt to open the Dfs Root's IPC$ share if we haven't already done
// so.
//
if (DnrContext->pService->ConnFile == NULL) {
HANDLE hDC;
SE_IMPERSONATION_STATE DisabledImpersonationState;
BOOLEAN RestoreImpersonationState = FALSE;
ExReleaseResourceLite(&DfsData.Resource);
KeQuerySystemTime(&EndTime);
#if DBG
if (MupVerbose)
DbgPrint(" [%d] Opening connection to [\\%wZ\\IPC$] using [%wZ]\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
&DnrContext->pService->Name,
&DnrContext->pProvider->DeviceName);
#endif
if (MupUseNullSessionForDfs) {
RestoreImpersonationState = PsDisableImpersonation(
PsGetCurrentThread(),
&DisabledImpersonationState);
}
Status = DfsCreateConnection(
DnrContext->pService,
DnrContext->pProvider,
(BOOLEAN)
((DnrContext->Vcb->VcbState & VCB_STATE_CSCAGENT_VOLUME) != 0),
&hDC);
if (RestoreImpersonationState) {
PsRestoreImpersonation(
PsGetCurrentThread(),
&DisabledImpersonationState);
}
#if DBG
if (MupVerbose)
DbgPrint(" Open of connection Status=0x%x\n", Status);
#endif
if (NT_SUCCESS( Status )) {
if (DnrContext->USN != DnrContext->pPktEntry->USN) {
//
// Dang, Pkt Entry changed when we made the
// connection. We'll have to retry.
//
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
ZwClose( hDC );
DnrContext->State = DnrStateGetFirstDC;
#if DBG
if (MupVerbose)
DbgPrint(" USN changed.\n");
#endif
break;
}
}
if ( NT_SUCCESS( Status ) ) {
PFILE_OBJECT FileObject; // Need stack based variable
// because ObRef... expects
// this parameter to be in
// non-paged memory.
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
if (DnrContext->pService->ConnFile == NULL) {
//
// 426184, need to check return code for errors.
//
Status = ObReferenceObjectByHandle(
hDC,
0,
NULL,
KernelMode,
(PVOID *)&FileObject,
NULL);
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrNameResolve_Error_ObReferenceObjectByHandle,
LOGSTATUS(Status)
LOGPTR(FileObject));
#if DBG
if (MupVerbose)
DbgPrint(" ObReferenceObjectByHandle returned 0x%x\n", Status);
#endif
if ( NT_SUCCESS( Status ) ) {
DnrContext->pService->ConnFile = FileObject;
}
ZwClose( hDC );
}
}
if ( NT_SUCCESS( Status ) ) {
DnrContext->DCConnFile = DnrContext->pService->ConnFile;
DnrContext->CachedConnFile = FALSE;
DFS_REFERENCE_OBJECT( DnrContext->DCConnFile );
ExReleaseResourceLite( &DfsData.Resource );
} else if (DfsEventLog > 0) {
LogWriteMessage(
DFS_CONNECTION_FAILURE,
Status,
1,
&DnrContext->pService->Name);
}
} else {
//
// DnrContext->pService is protected by the Pkt lock. Since we
// will be using pService->ConnFile to send the referral request,
// we better reference and cache it.
//
DnrContext->DCConnFile = DnrContext->pService->ConnFile;
DFS_REFERENCE_OBJECT( DnrContext->DCConnFile );
DnrContext->CachedConnFile = TRUE;
ExReleaseResourceLite(&DfsData.Resource);
Status = STATUS_SUCCESS;
}
//
// Unable to get IPC$ share, try the next Dfs root
//
if (!NT_SUCCESS(Status)) {
DnrContext->State = DnrStateGetNextDC;
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
break;
}
//
// Opened Dfs Root's IPC$ share - remember this DC is a good one.
//
ReplSetActiveService(
DnrContext->pPktEntry,
DnrContext->RDCSelectContext);
//
// Build the Referral request...
//
Irp = DnrBuildReferralRequest(DnrContext);
if (Irp == NULL) {
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
Irp = DnrContext->OriginalIrp;
DnrContext->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
DFS_DEREFERENCE_OBJECT(DnrContext->DCConnFile);
DnrContext->State = DnrStateDone;
#if DBG
if (MupVerbose)
DbgPrint(" DnrBuildReferralRequest returned NULL irp\n");
#endif
break;
}
DnrContext->State = DnrStateCompleteReferral;
PktRelease();
DnrContext->ReleasePkt = FALSE;
//
// The PktReferralRequests semaphore is used to control how
// many threads can simultaneously be going for referrals. The
// following Wait will decrement the PktReferralRequests
// semaphore by 1 if it is not already 0. If it is 0, then
// this thread will suspend until someone else bumps up the
// semaphore by 1. We will bump up the semaphore count in
// DnrCompleteReferral.
//
Status = KeWaitForSingleObject(
&DfsData.PktReferralRequests,
UserRequest, // WaitReason - don't care
KernelMode,
FALSE, // Alertable
NULL); // Timeout
ASSERT(Status == STATUS_SUCCESS);
KeQuerySystemTime(&EndTime);
#if DBG
if (MupVerbose)
DbgPrint(" [%d] asking for referral (IoCallDriver)\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
#endif
IoMarkIrpPending( DnrContext->OriginalIrp );
Status = IoCallDriver( DnrContext->TargetDevice, Irp );
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrNameResolve_Error_IoCallDriver,
LOGSTATUS(Status)
LOGPTR(FileObject));
//
// We now return STATUS_PENDING. DnrCompleteReferral will
// resume the Dnr.
//
DfsDbgTrace(-1, Dbg, "DnrNameResolve: returning %08lx\n", ULongToPtr(STATUS_PENDING));
return(STATUS_PENDING);
case DnrStateGetNextDC:
DfsDbgTrace(0, Dbg, "FSM State GetNextDC\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateGetNextDC\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
{
NTSTATUS ReplStatus;
PDFS_PKT_ENTRY pPktEntry = NULL;
UNICODE_STRING RemPath;
pPktEntry = PktLookupEntryByPrefix(&DfsData.Pkt,
&DnrContext->FileName,
&RemPath);
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
ReplStatus = ReplFindNextProvider(DnrContext->pPktEntry,
&DnrContext->pService,
&DnrContext->RDCSelectContext,
&LastEntry);
if (NT_SUCCESS(ReplStatus)) {
ASSERT(DnrContext->pService != NULL);
ASSERT(DnrContext->pService->pProvider != NULL);
DnrContext->pProvider = DnrContext->pService->pProvider;
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
DnrContext->State = DnrStateGetReferrals;
} else if (pPktEntry != NULL && pPktEntry->ExpireTime <= 0) {
//
// Extend the timeout on the stale entry we have
//
ASSERT( PKT_LOCKED_FOR_SHARED_ACCESS() );
#if DBG
if (MupVerbose) {
DbgPrint(" Out of roots to try for referral for [%wZ]\n",
&DnrContext->FileName);
DbgPrint(" Found stale referral [%wZ], adding 60 sec to it\n",
&pPktEntry->Id.Prefix);
}
#endif
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
DnrContext->State = DnrStateStart;
} else {
#if DBG
if (MupVerbose)
DbgPrint(" Out of roots to try for referral for [%wZ], no stale found\n",
&DnrContext->FileName);
#endif
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
DnrContext->FinalStatus = STATUS_CANT_ACCESS_DOMAIN_INFO;
DnrContext->State = DnrStateDone;
}
ExReleaseResourceLite(&DfsData.Resource);
}
break;
case DnrStateDone:
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateDone\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
Status = DnrContext->FinalStatus;
// FALL THROUGH ...
case DnrStateLocalCompletion:
DfsDbgTrace(0, Dbg, "FSM state Done\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint("[%d] FSM state DnrStateLocalCompletion\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
if (Status != STATUS_LOGON_FAILURE && !ReplIsRecoverableError(Status)) {
if (DnrContext->pPktEntry != NULL &&
(DnrContext->pPktEntry->Type & PKT_ENTRY_TYPE_SYSVOL) &&
(DnrContext->pPktEntry->Link.Flink == &(DnrContext->pPktEntry->Link))) {
PDFS_PKT Pkt = _GetPkt();
PDFS_PKT_ENTRY Entry = DnrContext->pPktEntry;
PDFS_PKT_ENTRY pMatchEntry;
InterlockedIncrement(&Entry->UseCount);
if (DnrContext->ReleasePkt)
PktRelease();
PktAcquireExclusive(TRUE, &DnrContext->ReleasePkt);
InterlockedDecrement(&Entry->UseCount);
#if DBG
if ((MupVerbose) && (pktEntry != NULL)) {
//
// Temporary debug stuff.
//
if ((pktEntry->NodeTypeCode != DSFS_NTC_PKT_ENTRY) ||
(pktEntry->NodeByteSize != sizeof(*pktEntry))) {
DbgPrint("DnrNameResolve: Updating bogus Pkt entry avoided: Pkt Entry 0x%x\n", pktEntry);
}
}
#endif
pMatchEntry = PktFindEntryByPrefix(
Pkt,
&Entry->Id.Prefix);
if ((Entry->Type & PKT_ENTRY_TYPE_DELETE_PENDING) == 0) {
if (pMatchEntry == NULL) {
if (DfsInsertUnicodePrefix(&Pkt->PrefixTable,
&Entry->Id.Prefix,
&Entry->PrefixTableEntry)) {
//
// We successfully created the prefix entry, so now we link
// this entry into the PKT.
//
PktLinkEntry(Pkt, Entry);
}
} else {
//
// Destroy this entry if it isn't in the table, as we are
// about to orphan it.
//
if (pMatchEntry != NULL && pMatchEntry != Entry) {
Entry->ActiveService = NULL;
PktEntryIdDestroy(&Entry->Id, FALSE);
PktEntryInfoDestroy(&Entry->Info, FALSE);
ExFreePool(Entry);
}
}
}
PktRelease();
DnrContext->ReleasePkt = FALSE;
}
}
if (DnrContext->ReleasePkt)
PktRelease();
if ((Status == STATUS_DEVICE_OFF_LINE) &&
(IrpSp->FileObject->RelatedFileObject == NULL)) {
Status = DfsRerouteOpenToMup(IrpSp->FileObject, &DnrContext->FileName);
}
if (DnrContext->FcbToUse != NULL) {
DfsDetachFcb(DnrContext->FcbToUse->FileObject, DnrContext->FcbToUse);
ExFreePool( DnrContext->FcbToUse );
}
DfsCompleteRequest(DnrContext->pIrpContext, Irp, Status);
DnrReleaseCredentials(DnrContext);
SeDeleteClientSecurity( &DnrContext->SecurityContext );
if (DnrContext->NameAllocated)
ExFreePool( DnrContext->FileName.Buffer );
if (DnrContext->pDfsTargetInfo != NULL)
{
PktReleaseTargetInfo(DnrContext->pDfsTargetInfo);
DnrContext->pDfsTargetInfo = NULL;
}
KeQuerySystemTime(&EndTime);
#if DBG
if (MupVerbose)
DbgPrint("[%d] DnrNameResolve exit 0x%x\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
Status);
#endif
DeallocateDnrContext(DnrContext);
DfsDbgTrace(-1, Dbg, "DnrNameResolve: Exit ->%x\n", ULongToPtr(Status));
return Status;
default:
BugCheck("DnrNameResolve: unexpected DNR state");
}
}
BugCheck("DnrNameResolve: unexpected exit from loop");
}
//+----------------------------------------------------------------------------
//
// Function: DnrComposeFileName
//
// Synopsis: Given a DFS_VCB (implicitly a Device Object), and a file name
// relative to that device, this routine will compose a fully
// qualified name (ie, a name relative to the highest (org) root).
//
// Arguments: [FullName] -- Fully qualified name destination.
// [Vcb] -- Pointer to Vcb of Device Object.
// [RelatedFile] -- Related file object.
// [FileName] -- The file being "name resolved"
//
// Returns:
//
// Note: This function assumes that file names are composed precisely
// of two parts - the name relative to org of the file object's
// device, followed by the name of the file relative to the device
// This may not be true if we have a related file object! In that
// case, the full name is three part - device name relative to
// org, related file name relative to device, and file name
// relative to related file. However, in create.c,
// DfsCommonCreate, we manipulate file objects so all opens look
// like "non-relative" opens. If one changes that code, then
// this function must be changed to correspond.
//
//-----------------------------------------------------------------------------
VOID
DnrComposeFileName(
OUT PUNICODE_STRING FullName,
IN PDFS_VCB Vcb,
IN PFILE_OBJECT RelatedFile,
IN PUNICODE_STRING FileName
)
{
PUNICODE_STRING LogRootPrefix = &(Vcb->LogRootPrefix);
ASSERT(FullName->MaximumLength >= FileName->Length + LogRootPrefix->Length);
ASSERT(FullName->Length == 0);
ASSERT(FullName->Buffer != NULL);
if ((LogRootPrefix->Length > 0) && (RelatedFile == NULL)) {
RtlMoveMemory(FullName->Buffer, LogRootPrefix->Buffer,
LogRootPrefix->Length);
FullName->Length = LogRootPrefix->Length;
} else {
FullName->Buffer[0] = UNICODE_PATH_SEP;
FullName->Length = sizeof(UNICODE_PATH_SEP);
}
DnrConcatenateFilePath(
FullName,
FileName->Buffer,
FileName->Length);
}
//+----------------------------------------------------------------------------
//
// Function: DnrCaptureCredentials
//
// Synopsis: Captures the credentials to use for Dnr.
//
// Arguments: [DnrContext] -- The DNR_CONTEXT record describing the Dnr.
//
// Returns: Nothing -- The DnrContext is simply updated.
//
//-----------------------------------------------------------------------------
VOID
DnrCaptureCredentials(
IN OUT PDNR_CONTEXT DnrContext)
{
#ifdef TERMSRV
NTSTATUS Status;
ULONG SessionID;
#endif // TERMSRV
LUID LogonID;
DfsDbgTrace(+1, Dbg, "DnrCaptureCredentials: Enter [%wZ] \n", &DnrContext->FileName);
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
DfsGetLogonId( &LogonID );
#ifdef TERMSRV
Status = TSGetRequestorSessionId( DnrContext->OriginalIrp, & SessionID );
ASSERT( NT_SUCCESS( Status ) ) ;
if( NT_SUCCESS( Status ) ) {
DnrContext->Credentials = DfsLookupCredentials( &DnrContext->FileName, SessionID, &LogonID );
}
else {
DnrContext->Credentials = NULL;
}
#else // TERMSRV
DnrContext->Credentials = DfsLookupCredentials( &DnrContext->FileName, &LogonID );
#endif // TERMSRV
if (DnrContext->Credentials != NULL)
DnrContext->Credentials->RefCount++;
ExReleaseResourceLite( &DfsData.Resource );
DfsDbgTrace(-1, Dbg, "DnrCaptureCredentials: Exit. Creds %x\n", DnrContext->Credentials);
}
//+----------------------------------------------------------------------------
//
// Function: DnrReleaseCredentials
//
// Synopsis: Releases the credentials captured by DnrCaptureCredentials
//
// Arguments: [DnrContext] -- The DNR_CONTEXT into which credentials were
// captured.
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
VOID
DnrReleaseCredentials(
IN PDNR_CONTEXT DnrContext)
{
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
if (DnrContext->Credentials != NULL)
DnrContext->Credentials->RefCount--;
ExReleaseResourceLite( &DfsData.Resource );
}
//+-------------------------------------------------------------------
//
// Function: DnrRedirectFileOpen, local
//
// Synopsis: This routine redirects a create IRP request to the specified
// provider by doing an IoCallDriver to the device object for
// which the file open is destined. This routine takes care of
// converting the FileObject's name from the Dfs namespace to
// the underlying file system's namespace.
//
// Arguments: [DnrContext] -- The context block for the DNR. All
// parameters for the operation will be taken from
// here.
//
// Returns: [STATUS_DEVICE_OFF_LINE] -- The service for the volume
// is currently off line.
//
// [STATUS_DEVICE_NOT_CONNECTED] -- The storage for the volume
// is not available at this time. Might have been blown
// off by a format etc.
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Unable to allocate room
// for the file name that the provider for this volume
// understands.
//
// [STATUS_PENDING] -- If the underlying file system returned
// STATUS_PENDING.
//
// Any other NTSTATUS that the underlying file system returned.
//
// Notes:
//
//--------------------------------------------------------------------
NTSTATUS
DnrRedirectFileOpen (
IN PDNR_CONTEXT DnrContext
) {
PIRP Irp = DnrContext->OriginalIrp;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PIO_STACK_LOCATION NextIrpSp = NULL;
PFILE_OBJECT FileObject = IrpSp->FileObject;
PDFS_VCB Vcb = DnrContext->Vcb;
NTSTATUS Status;
UNICODE_STRING fileName;
ULONG CreateOptions;
PPROVIDER_DEF pProvider;
UNICODE_STRING ProviderDeviceName;
DfsDbgTrace(+1, Dbg, "DnrRedirectFileOpen: Entered\n", 0);
MUP_TRACE_NORM(DNR, DnrRedirectFileOpen_Entry,
LOGPTR(DnrContext->OriginalIrp)
LOGUSTR(DnrContext->FileName));
//
// If this is a csc agent open, force the open to the LanManRedirector,
// as this is the only redirector that works with csc.
//
DNR_SET_TARGET_INFO( DnrContext, DnrContext->pPktEntry );
DnrContext->DfsNameContext.pLMRTargetInfo = NULL;
DnrContext->DfsNameContext.pDfsTargetInfo = NULL;
if (DnrContext->pDfsTargetInfo != NULL) {
if (DnrContext->pDfsTargetInfo->DfsHeader.Flags & TARGET_INFO_DFS)
{
DnrContext->DfsNameContext.pDfsTargetInfo =
(PVOID)&DnrContext->pDfsTargetInfo->TargetInfo;
}
else {
DnrContext->DfsNameContext.pLMRTargetInfo =
(PVOID)&DnrContext->pDfsTargetInfo->LMRTargetInfo;
}
}
if (
(DnrContext->Vcb->VcbState & VCB_STATE_CSCAGENT_VOLUME)
&&
(DnrContext->pService->Type & DFS_SERVICE_TYPE_DOWN_LEVEL)
) {
RtlInitUnicodeString(&ProviderDeviceName, DD_NFS_DEVICE_NAME_U);
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
Status = DfsGetProviderForDevice(
&ProviderDeviceName,
&DnrContext->pProvider);
if (NT_SUCCESS( Status )) {
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
if (MupVerbose)
DbgPrint(" CSCAGENT:Provider Device [%wZ] -> [%wZ]\n",
&DnrContext->TargetDevice->DriverObject->DriverName,
&DnrContext->pProvider->DeviceObject->DriverObject->DriverName);
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
} else {
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
ExReleaseResourceLite( &DfsData.Resource );
DnrReleaseAuthenticatedConnection(DnrContext);
DnrContext->FinalStatus = STATUS_BAD_NETWORK_PATH;
DnrContext->State = DnrStateDone;
return(STATUS_BAD_NETWORK_PATH);
}
ExReleaseResourceLite( &DfsData.Resource );
}
//
// Prepare to hand of the open request to the next driver. We
// must give it a name that it will understand; so, we save the original
// file name in the DnrContext in case we need to restore it in the
// event of a failure.
//
DnrContext->SavedFileName = FileObject->FileName;
DnrContext->SavedRelatedFileObject = FileObject->RelatedFileObject;
ASSERT( DnrContext->SavedFileName.Buffer != NULL );
//
// Create the full path name to be opened from the target device
// object.
//
fileName.MaximumLength =
DnrContext->pService->Address.Length +
DnrContext->pPktEntry->Id.Prefix.Length +
sizeof (WCHAR) +
DnrContext->RemainingPart.Length;
fileName.Buffer = ExAllocatePoolWithTag(PagedPool, fileName.MaximumLength, ' puM');
if (fileName.Buffer == NULL) {
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
DnrReleaseAuthenticatedConnection(DnrContext);
DnrContext->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
DnrContext->State = DnrStateDone;
return(STATUS_INSUFFICIENT_RESOURCES);
}
if (DnrContext->pService->Address.Buffer) {
RtlMoveMemory( fileName.Buffer,
DnrContext->pService->Address.Buffer,
DnrContext->pService->Address.Length
);
fileName.Length = DnrContext->pService->Address.Length;
} else {
fileName.Buffer[0] = UNICODE_PATH_SEP;
fileName.Length = sizeof(WCHAR);
}
//
// If we are supposed to strip the prefix, do it now.
//
if (!(DnrContext->pService->Capability & PROV_STRIP_PREFIX)) {
DnrConcatenateFilePath(
&fileName,
DnrContext->pPktEntry->Id.Prefix.Buffer,
DnrContext->pPktEntry->Id.Prefix.Length);
}
if (DnrContext->RemainingPart.Length > 0) {
DnrConcatenateFilePath(
&fileName,
DnrContext->RemainingPart.Buffer,
DnrContext->RemainingPart.Length);
}
DnrContext->NewNameLen = fileName.Length;
//
// Attempt to open the file. Copy all of the information
// from the create IRP we received.
//
DfsDbgTrace( 0, Dbg, "Attempt to open %wZ\n", &fileName );
//
// Copy the stack from one to the next...
//
NextIrpSp = IoGetNextIrpStackLocation(Irp);
(*NextIrpSp) = (*IrpSp);
CreateOptions = IrpSp->Parameters.Create.Options;
// Update the type of open in the DfsNameContext
if (DnrContext->Vcb->VcbState & VCB_STATE_CSCAGENT_VOLUME) {
#if DBG
if (MupVerbose)
DbgPrint(" FsContext = DFS_CSCAGENT_NAME_CONTEXT\n");
#endif
DnrContext->DfsNameContext.NameContextType = DFS_CSCAGENT_NAME_CONTEXT;
} else {
#if DBG
if (MupVerbose)
DbgPrint(" FsContext = DFS_USER_NAME_CONTEXT\n");
#endif
DnrContext->DfsNameContext.NameContextType = DFS_USER_NAME_CONTEXT;
}
FileObject->FsContext = &(DnrContext->DfsNameContext);
if (DnrContext->pProvider->fProvCapability & PROV_DFS_RDR) {
//
// We are connecting to a dfs-aware server. Indicate this to the
// redirector.
//
FileObject->FsContext2 = UIntToPtr(DFS_OPEN_CONTEXT);
} else {
//
// We are connecting to a downlevel server. Indicate to the redirector
// that Dfs is trying a downlevel access.
//
FileObject->FsContext2 = UIntToPtr(DFS_DOWNLEVEL_OPEN_CONTEXT);
}
NextIrpSp->Parameters.Create.Options = CreateOptions;
FileObject->RelatedFileObject = NULL;
FileObject->FileName = fileName;
IoSetCompletionRoutine(
Irp,
DnrCompleteFileOpen,
DnrContext,
TRUE,
TRUE,
TRUE);
//
// Now, we are going to pass the buck to the provider for this volume.
// This can potentially go over the net. To avoid needless contentions,
// we release the Pkt.
//
ASSERT( PKT_LOCKED_FOR_SHARED_ACCESS() );
InterlockedIncrement(&DnrContext->pPktEntry->UseCount);
#if defined (USECOUNT_DBG)
{
LONG Count;
Count = InterlockedIncrement(&DnrContext->pService->pMachEntry->UseCount);
if (Count < DnrContext->pService->pMachEntry->SvcUseCount)
{
DbgPrint("DnrContext %x, 1\n", DnrContext);
DfsDbgBreakPoint;
}
}
#else
InterlockedIncrement(&DnrContext->pService->pMachEntry->UseCount);
#endif
DnrContext->FcbToUse->DfsMachineEntry = DnrContext->pService->pMachEntry;
DnrContext->FcbToUse->TargetDevice = DnrContext->TargetDevice;
DnrContext->FcbToUse->ProviderId = DnrContext->ProviderId;
PktRelease();
DnrContext->ReleasePkt = FALSE;
#if DBG
if (MupVerbose)
DbgPrint(" DnrRedirectFileOpen of [%wZ(%wZ):0x%x] to [%wZ]\n",
&fileName,
&DnrContext->DfsNameContext.UNCFileName,
DnrContext->DfsNameContext.Flags,
&DnrContext->TargetDevice->DriverObject->DriverName);
#endif
MUP_TRACE_NORM(DNR, DnrRedirectFileOpen_BeforeIoCallDriver,
LOGPTR(Irp)
LOGUSTR(DnrContext->FileName)
LOGUSTR(DnrContext->TargetDevice->DriverObject->DriverName));
Status = IoCallDriver(DnrContext->TargetDevice, Irp);
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrRedirectFileOpen_Error_IoCallDriver,
LOGSTATUS(Status)
LOGPTR(Irp)
LOGPTR(FileObject)
LOGPTR(DnrContext));
if (Status != STATUS_PENDING) {
DnrContext->State = DnrStatePostProcessOpen;
}
DfsDbgTrace( 0, Dbg, "IoCallDriver Status = %8lx\n", ULongToPtr(Status));
DfsDbgTrace(-1, Dbg, "DnrRedirectFileOpen: Exit -> %x\n", ULongToPtr(Status));
return(Status);
}
//+-------------------------------------------------------------------
//
// Function: DnrPostProcessFileOpen, local
//
// Synopsis: This routine picks up where DnrRedirectFileOpen left off.
// It figures out what the underlying file system returned
// in response to our IoCallDriver, and resumes DNR from there.
//
// Arguments: [DnrContext] -- The context block for the DNR. All
// parameters for the operation will be taken from
// here.
//
// Returns: NTSTATUS - The status of the operation.
//
//--------------------------------------------------------------------
ULONG StopOnError = 0;
NTSTATUS
DnrPostProcessFileOpen(
IN PDNR_CONTEXT DnrContext)
{
NTSTATUS Status;
PDFS_VCB Vcb = DnrContext->Vcb;
PIRP Irp = DnrContext->OriginalIrp;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
LARGE_INTEGER EndTime;
DfsDbgTrace( +1, Dbg, "DnrPostProcessFileOpen Entered: DnrContext = %08lx\n",
DnrContext );
Status = DnrContext->FinalStatus;
if ((Status == STATUS_LOGON_FAILURE) || (Status == STATUS_ACCESS_DENIED))
{
if (MupVerbose)
{
DbgPrint("File %wZ, (%wZ), Status %x\n",
&DnrContext->ContextFileName, &FileObject->FileName, Status);
DbgPrint("Context used was %x, %x\n", DnrContext->DfsNameContext.pDfsTargetInfo,
DnrContext->DfsNameContext.pLMRTargetInfo );
DbgPrint("Driver is %wZ\n", &DnrContext->TargetDevice->DriverObject->DriverName);
}
}
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] DnrPostProcessFileOpen entered [%wZ] Status = 0x%x\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
&FileObject->FileName,
Status);
}
#endif
if ( Status == STATUS_REPARSE ) {
//
// This may have been an open sent to the MUP, who is now returning a status
// reparse. Figure out the name of the device that this is being
// reparsed to, create (if needed) a PROVIDER_DEF for this new device,
// and retry DnrRedirectFileOpen. Also, update the service
// structure to point to this new provider.
//
//
// If the device is not mup, clean out any child entries for the pkt
// entry that represents the root of the dfs, then stop dnr with
// STATUS_REPARSE
//
PDFS_PKT_ENTRY pEntry;
UNICODE_STRING ProviderDevice;
UNICODE_STRING MupDeviceName;
UNICODE_STRING FileName;
UNICODE_STRING RemPath;
USHORT i, j;
DfsDbgTrace(0, Dbg, "Processing STATUS_REPARSE...\n", 0);
ProviderDevice = FileObject->FileName;
RtlInitUnicodeString(&MupDeviceName, L"\\FileSystem\\Mup");
#if DBG
if (MupVerbose)
DbgPrint(" Comparing [%wZ] to [%wZ]\n",
&DnrContext->TargetDevice->DriverObject->DriverName,
&MupDeviceName);
#endif
if ( RtlCompareUnicodeString(
&DnrContext->TargetDevice->DriverObject->DriverName,
&MupDeviceName,
TRUE) != 0
) {
//
// This is *not* the mup returning REPARSE
//
FileName = DnrContext->FileName;
//
// We want to work with the \Server\Share part of the FileName only,
// so count up to 3 backslashes, then stop.
//
for (i = j = 0; i < FileName.Length/sizeof(WCHAR) && j < 3; i++) {
if (FileName.Buffer[i] == UNICODE_PATH_SEP) {
j++;
}
}
FileName.Length = (j >= 3) ? (i-1) * sizeof(WCHAR) : i * sizeof(WCHAR);
#if DBG
if (MupVerbose)
DbgPrint(" Will remove all children of [%wZ]\n", &FileName);
#endif
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
//
// Now find the pkt entry
//
pEntry = PktLookupEntryByPrefix(
&DfsData.Pkt,
&FileName,
&RemPath);
//
// And remove all children
//
if (pEntry != NULL) {
PktFlushChildren(pEntry);
}
PktRelease();
DnrContext->ReleasePkt = FALSE;
DnrContext->GotReparse = TRUE;
DnrContext->State = DnrStateDone;
Status = STATUS_REPARSE;
} else {
//
// This is the mup returning REPARSE
//
//
// We want to work with the \Device\Driver part of the ProviderDevice only,
// so count up to 3 backslashes, then stop.
//
for (i = j = 0; i < ProviderDevice.Length/sizeof(WCHAR) && j < 3; i++) {
if (ProviderDevice.Buffer[i] == UNICODE_PATH_SEP) {
j++;
}
}
ProviderDevice.Length = (j >= 3) ? (i-1) * sizeof(WCHAR) : i * sizeof(WCHAR);
DfsDbgTrace(0, Dbg, "Provider Device is [%wZ]\n", &ProviderDevice);
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
Status = DfsGetProviderForDevice(
&ProviderDevice,
&DnrContext->pProvider);
if (NT_SUCCESS( Status )) {
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
DnrContext->State = DnrStateSendRequest;
} else {
if (DnrContext->FinalStatus != STATUS_REPARSE) {
DnrContext->FinalStatus = Status;
}
DnrContext->State = DnrStateDone;
}
ExReleaseResourceLite( &DfsData.Resource );
}
ASSERT(DnrContext->ReleasePkt == FALSE);
//
// Set active service only if we're going to
// continue DNR, and the USN hasn't changed.
//
// The exit code (see comment at end) will restart DNR if the USN
// has changed.
//
if (NT_SUCCESS( Status )) {
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
if (DnrContext->USN == DnrContext->pPktEntry->USN) {
ReplSetActiveService(DnrContext->pPktEntry,
DnrContext->RSelectContext);
DnrContext->pService->ProviderId =
DnrContext->pProvider->eProviderId;
DnrContext->pService->pProvider = DnrContext->pProvider;
}
PktConvertExclusiveToShared();
} else {
if (Status == STATUS_FS_DRIVER_REQUIRED) {
Status = STATUS_REPARSE;
}
DnrContext->FinalStatus = Status;
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
}
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
#if defined (USECOUNT_DBG)
{
LONG Count;
Count = InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
if (Count < DnrContext->FcbToUse->DfsMachineEntry->SvcUseCount)
{
DbgPrint("DnrContext %x, 2\n", DnrContext);
DfsDbgBreakPoint;
}
}
#else
InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
#endif
DfsDbgTrace(0, Dbg, "State after Reparse is %d\n", DnrContext->State);
}
else if (( Status == STATUS_LOGON_FAILURE ) || (Status == STATUS_ACCESS_DENIED))
{
UNICODE_STRING ProviderDeviceName;
UNICODE_STRING MupDeviceName;
UNICODE_STRING ProviderDevice;
BOOLEAN ReturnError = FALSE;
NTSTATUS SavedStatus = Status;
ProviderDevice = FileObject->FileName;
RtlInitUnicodeString(&MupDeviceName, L"\\FileSystem\\Mup");
if ( RtlCompareUnicodeString(
&DnrContext->TargetDevice->DriverObject->DriverName,
&MupDeviceName,
TRUE) == 0 )
{
RtlInitUnicodeString(&ProviderDeviceName, DD_NFS_DEVICE_NAME_U);
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
Status = DfsGetProviderForDevice( &ProviderDeviceName,
&DnrContext->pProvider);
if (Status == STATUS_SUCCESS) {
DnrContext->ProviderId = DnrContext->pProvider->eProviderId;
DnrContext->TargetDevice = DnrContext->pProvider->DeviceObject;
DFS_REFERENCE_OBJECT(DnrContext->TargetDevice);
DnrContext->State = DnrStateSendRequest;
}
ExReleaseResourceLite( &DfsData.Resource );
if (Status == STATUS_SUCCESS)
{
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
if (DnrContext->USN == DnrContext->pPktEntry->USN) {
ReplSetActiveService(DnrContext->pPktEntry,
DnrContext->RSelectContext);
DnrContext->pService->ProviderId =
DnrContext->pProvider->eProviderId;
DnrContext->pService->pProvider = DnrContext->pProvider;
}
PktConvertExclusiveToShared();
}
}
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
if (Status != STATUS_SUCCESS)
{
DnrContext->FinalStatus = SavedStatus;
DnrContext->State = DnrStateDone;
ExFreePool( FileObject->FileName.Buffer );
FileObject->FileName = DnrContext->SavedFileName;
FileObject->RelatedFileObject = DnrContext->SavedRelatedFileObject;
}
}
else if ( Status == STATUS_OBJECT_TYPE_MISMATCH ) {
//
// This was an open sent to a downlevel server\share that failed
// because the server happens to be in a Dfs itself. If so, we
// simply change the name on which we are doing DNR and restart DNR.
//
DfsDbgTrace(0, Dbg, "Downlevel access found inter-dfs link!\n", 0);
DfsDbgTrace(
0, Dbg, "Current File name is [%wZ]\n", &FileObject->FileName);
ASSERT(DnrContext->ReleasePkt == FALSE);
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
//
// Bug: 332061. do not mark it as outside my dom.
//
// DnrContext->pPktEntry->Type |= PKT_ENTRY_TYPE_OUTSIDE_MY_DOM;
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
#if defined (USECOUNT_DBG)
{
LONG Count;
Count = InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
if (Count < DnrContext->FcbToUse->DfsMachineEntry->SvcUseCount)
{
DbgPrint("DnrContext %x, 3\n", DnrContext);
DfsDbgBreakPoint;
}
}
#else
InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
#endif
DnrContext->RemainingPart.Length = 0;
DnrContext->RemainingPart.MaximumLength = 0;
DnrContext->RemainingPart.Buffer = 0;
DnrRebuildDnrContext(
DnrContext,
&FileObject->FileName,
&DnrContext->RemainingPart);
ExFreePool(FileObject->FileName.Buffer);
FileObject->FileName = DnrContext->SavedFileName;
FileObject->RelatedFileObject = DnrContext->SavedRelatedFileObject;
} else if ( NT_SUCCESS( Status ) ) {
PDFS_FCB Fcb;
DfsDbgTrace( 0, Dbg, "Open attempt succeeded\n", 0 );
ASSERT( (DnrContext->FileName.Length & 0x1) == 0 );
Fcb = DnrContext->FcbToUse;
DnrContext->FcbToUse = NULL;
DfsDbgTrace(0, Dbg, "Fcb = %08lx\n", Fcb);
Fcb->TargetDevice = DnrContext->TargetDevice;
Fcb->ProviderId = DnrContext->ProviderId;
//
// If we file (dir) happens to be a junction point, we capture its
// alternate name from the Pkt Entry, so we can field requests for
// FileAlternateNameInformation.
//
if (DnrContext->RemainingPart.Length == 0) {
UNICODE_STRING allButLast;
RemoveLastComponent(
&DnrContext->pPktEntry->Id.ShortPrefix,
&allButLast);
Fcb->AlternateFileName.Length =
DnrContext->pPktEntry->Id.ShortPrefix.Length -
allButLast.Length;
RtlCopyMemory(
Fcb->AlternateFileName.Buffer,
&DnrContext->pPktEntry->Id.ShortPrefix.Buffer[
allButLast.Length/sizeof(WCHAR)],
Fcb->AlternateFileName.Length);
DfsDbgTrace(
0, Dbg, "Captured alternate name [%wZ]\n",
&Fcb->AlternateFileName);
}
InterlockedIncrement(&Fcb->Vcb->OpenFileCount);
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
if (DnrContext->USN == DnrContext->pPktEntry->USN) {
ReplSetActiveService(DnrContext->pPktEntry,
DnrContext->RSelectContext);
}
//
// Reset the life time since we just used this PKT entry successfully.
//
DnrContext->pPktEntry->ExpireTime = DnrContext->pPktEntry->TimeToLive;
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
PktConvertExclusiveToShared();
DnrContext->FinalStatus = Status;
DnrContext->State = DnrStateDone;
ExFreePool( DnrContext->SavedFileName.Buffer );
} else { // ! NT_SUCCESS( Status ) on IoCallDriver
DfsDbgTrace( 0, Dbg, "Open attempt failed %8lx\n", ULongToPtr(Status) );
if (Status == STATUS_PATH_NOT_COVERED || Status == STATUS_DFS_EXIT_PATH_FOUND) {
if (DnrContext->GotReferral) {
//
// We just got a referral, and the server is saying
// path_not_covered. Means DC and server are out
// of sync. Inform the DC
//
DfsDbgTrace(0, Dbg, "Dnr: Knowledge inconsistency discovered %wZ\n",
&FileObject->FileName);
(VOID) DfsTriggerKnowledgeVerification( DnrContext );
//
// If we never found an inconsistency let us now
// go back and try to see if we got this fixed.
// We won't be in an endless loop since we will
// not do this more than once.
//
if (DnrContext->FoundInconsistency == FALSE) {
DnrContext->State = DnrStateGetFirstReplica;
DnrContext->FoundInconsistency = TRUE;
} else
DnrContext->State = DnrStateGetNextReplica;
} else {
DnrContext->State = DnrStateGetFirstDC;
}
} else if (ReplIsRecoverableError( Status )) {
//
// Check to see if the error returned was something worth
// trying a replica for.
//
DnrContext->State = DnrStateGetNextReplica;
#if DBG
if (MupVerbose)
DbgPrint(" Recoverable error 0x%x State = DnrStateGetNextReplica\n", Status);
#endif
}
else if ((Status == STATUS_OBJECT_PATH_NOT_FOUND) &&
(DnrContext->RemainingPart.Length == 0)) {
//
// if we hit a PATH_NOT_FOUND at the root, it usually means
// that we have hit a machine that is no longer the root.
// If there is only one target, mark it as expired.
// otherwise, move on to the other targets.
//
if (DnrContext->pPktEntry->Info.ServiceCount > 1)
{
DnrContext->State = DnrStateGetNextReplica;
}
else
{
DnrContext->pPktEntry->ExpireTime = 0;
DnrContext->FinalStatus = Status;
DnrContext->State = DnrStateDone;
}
} else {
DnrContext->FinalStatus = Status;
DnrContext->State = DnrStateDone;
#if DBG
if (MupVerbose)
DbgPrint(" NON-Recoverable error 0x%x State = DnrStateDone\n", Status);
#endif
}
if (DfsEventLog > 0) {
UNICODE_STRING puStr[2];
if (!DnrContext->ReleasePkt)
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
if (DnrContext->USN == DnrContext->pPktEntry->USN) {
puStr[0] = FileObject->FileName;
puStr[1] = DnrContext->pService->Name;
LogWriteMessage(DFS_OPEN_FAILURE, Status, 2, puStr);
}
}
//
// In either case, we are going back into DNR. Let's acquire shared
// access to Pkt. We had released the Pkt just before doing the
// IoCallDriver.
//
if (!DnrContext->ReleasePkt)
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
#if defined (USECOUNT_DBG)
{
LONG Count;
Count = InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
if (Count < DnrContext->FcbToUse->DfsMachineEntry->SvcUseCount)
{
DbgPrint("DnrContext %x, 4\n", DnrContext);
DfsDbgBreakPoint;
}
}
#else
InterlockedDecrement(&DnrContext->FcbToUse->DfsMachineEntry->UseCount);
#endif
ExFreePool( FileObject->FileName.Buffer );
FileObject->FileName = DnrContext->SavedFileName;
FileObject->RelatedFileObject = DnrContext->SavedRelatedFileObject;
}
//
// One last thing. If we are going back into DNR for whatever reason,
// check to see if the PktEntry we captured in the DNR_CONTEXT has
// changed. If so, we'll simply have to restart.
//
if (DnrContext->State != DnrStateDone &&
DnrContext->pPktEntry != NULL &&
DnrContext->pPktEntry->USN != DnrContext->USN) {
DnrContext->State = DnrStateStart;
}
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] DnrPostProcessFileOpen Exited: Status = %08lx State = %d\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
Status,
DnrContext->State);
}
#endif
DfsDbgTrace( -1, Dbg, "DnrPostProcessFileOpen Exited: Status = %08lx\n",
ULongToPtr(Status) );
return Status;
}
//+----------------------------------------------------------------------------
//
// Function: DnrGetAuthenticatedConnection
//
// Synopsis: If this Dnr is using user-supplied credentials, this routine
// will setup a tree connection using the user-supplied
// credentials.
//
// Notes: This routine might free and reacquire the Pkt lock. This
// means that the Pkt entry referenced in DnrContext might
// become invalid after this call. The caller is assumed to
// have cached and referenced everything she will need to
// use in DnrContext before making this call.
//
// Arguments: [DnrContext] -- The DNR_CONTEXT record for this Dnr
//
// Returns: [STATUS_SUCCESS] -- Operation completed successfully
//
// NT Status from the attempt to create the tree connection
//
//-----------------------------------------------------------------------------
NTSTATUS
DnrGetAuthenticatedConnection(
IN OUT PDNR_CONTEXT DnrContext)
{
NTSTATUS Status;
PDFS_SERVICE pService = DnrContext->pService;
BOOLEAN fDoConnection = TRUE;
LARGE_INTEGER EndTime;
PFILE_OBJECT TreeConnFileObj = NULL;
DfsDbgTrace(+1, Dbg, "DnrGetAuthenticatedConnection: Entered\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] DnrGetAuthenticatedConnection(\\%wZ)\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
&pService->Address);
}
#endif
ASSERT(DnrContext->pService != NULL);
ASSERT(DnrContext->pProvider != NULL);
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
//
// See if we are using supplied credentials
//
if (DnrContext->Credentials == NULL) {
DfsDbgTrace(-1, Dbg,
"DnrGetAuthenticatedConnection: Dnr with no creds\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] DnrGetAuthenticatedConnection: No creds exit STATUS_SUCCESS\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
ExReleaseResourceLite( &DfsData.Resource );
return( STATUS_SUCCESS );
}
//
// See if this is a credential record describing the use of default
// credentials
//
if (DnrContext->Credentials->EaLength == 0) {
DfsDbgTrace(-1, Dbg,
"DnrGetAuthenticatedConnection: Dnr with default creds\n", 0);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] DnrGetAuthenticatedConnection: Default creds exit STATUS_SUCCESS\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
ExReleaseResourceLite( &DfsData.Resource );
return( STATUS_SUCCESS );
}
//
// See if we already have a authenticated connection to the server, and
// the authenticated connection was established using the credentials
// we want to use.
//
if (pService->pMachEntry->AuthConn != NULL) {
if (
(DnrContext->Vcb->VcbState & VCB_STATE_CSCAGENT_VOLUME) != 0
&&
pService->pMachEntry->Credentials == DnrContext->Credentials
) {
DnrContext->AuthConn = pService->pMachEntry->AuthConn;
DFS_REFERENCE_OBJECT( DnrContext->AuthConn );
fDoConnection = FALSE;
DfsDbgTrace(0, Dbg,
"Using existing tree connect %08lx\n", DnrContext->AuthConn);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] Using existing tree connect\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
Status = STATUS_SUCCESS;
} else {
DfsDbgTrace(0, Dbg,
"Deleting connect %08lx\n", pService->pMachEntry->AuthConn);
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] Deleting tree connect connect\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)));
}
#endif
TreeConnFileObj = pService->pMachEntry->AuthConn;
pService->pMachEntry->AuthConn = NULL;
pService->pMachEntry->Credentials->RefCount--;
pService->pMachEntry->Credentials = NULL;
if (pService->ConnFile != NULL)
DfsCloseConnection( pService );
}
}
ExReleaseResourceLite( &DfsData.Resource );
//
// We delete the tree connection after releasing the resource, since
// the delete involve a call to a lower level driver and we want to
// avoid resource lock conflicts.
//
if (TreeConnFileObj) {
DfsDeleteTreeConnection( TreeConnFileObj, USE_FORCE );
}
//
// If we need to establish a new authenticated connection, do it now.
// We need a new connection because either we had none, or the one we
// had was using a different set of credentials.
//
if (fDoConnection) {
UNICODE_STRING shareName;
HANDLE treeHandle;
OBJECT_ATTRIBUTES objectAttributes;
IO_STATUS_BLOCK ioStatusBlock;
USHORT i, k;
//
// Compute the share name...
//
shareName.MaximumLength =
sizeof(DD_NFS_DEVICE_NAME_U) +
pService->Address.Length;
shareName.Buffer = ExAllocatePoolWithTag(PagedPool, shareName.MaximumLength, ' puM');
if (shareName.Buffer != NULL) {
shareName.Length = 0;
RtlAppendUnicodeToString(
&shareName,
DD_NFS_DEVICE_NAME_U);
RtlAppendUnicodeStringToString(&shareName, &pService->Address);
//
// One can only do tree connects to server\share. So, in case
// pService->Address refers to something deeper than the share,
// make sure we setup a tree-conn only to server\share. Note that
// by now, shareName is of the form
// \Device\LanmanRedirector\server\share<\path>. So, count up to
// 4 slashes and terminate the share name there.
//
for (i = 0, k = 0;
i < shareName.Length/sizeof(WCHAR) && k < 5;
i++) {
if (shareName.Buffer[i] == UNICODE_PATH_SEP)
k++;
}
shareName.Length = i * sizeof(WCHAR);
if (k == 5)
shareName.Length -= sizeof(WCHAR);
InitializeObjectAttributes(
&objectAttributes,
&shareName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
DfsDbgTrace(0, Dbg, "Tree connecting to %wZ\n", &shareName);
DfsDbgTrace(0, Dbg,
"Credentials @%08lx\n", DnrContext->Credentials);
Status = ZwCreateFile(
&treeHandle,
SYNCHRONIZE,
&objectAttributes,
&ioStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_CREATE_TREE_CONNECTION |
FILE_SYNCHRONOUS_IO_NONALERT,
(PVOID) DnrContext->Credentials->EaBuffer,
DnrContext->Credentials->EaLength);
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrGetAuthenticatedConnection_Error_ZwCreateFile,
LOGSTATUS(Status));
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] Tree connect to [%wZ] returned 0x%x\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
&shareName,
Status);
}
#endif
if (NT_SUCCESS(Status)) {
PFILE_OBJECT fileObject;
DfsDbgTrace(0, Dbg, "Tree connect succeeded\n", 0);
//
// 426184, need to check return code for errors.
//
Status = ObReferenceObjectByHandle(
treeHandle,
0,
NULL,
KernelMode,
(PVOID *)&fileObject,
NULL);
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DnrGetAuthenticatedConnection_Error_ObReferenceObjectByHandle,
LOGSTATUS(Status));
ZwClose( treeHandle );
if (NT_SUCCESS(Status)) {
DnrContext->AuthConn = fileObject;
}
}
if (NT_SUCCESS(Status)) {
//
// We have a new tree connect. Lets try to cache it for later
// use. Note that the Pkt could have changed when we went out
// over the net to establish the tree connect, so we cache
// the tree connect only if the Pkt hasn't changed.
//
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
if (DnrContext->USN == DnrContext->pPktEntry->USN) {
if (pService->pMachEntry->AuthConn == NULL) {
pService->pMachEntry->AuthConn = DnrContext->AuthConn;
DFS_REFERENCE_OBJECT( pService->pMachEntry->AuthConn );
pService->pMachEntry->Credentials =
DnrContext->Credentials;
pService->pMachEntry->Credentials->RefCount++;
}
}
ExReleaseResourceLite( &DfsData.Resource );
DnrContext->ReleasePkt = FALSE;
PktRelease();
}
ExFreePool( shareName.Buffer );
} else {
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
DfsDbgTrace(-1, Dbg,
"DnrGetAuthenticatedConnection: Exit %08lx\n", ULongToPtr(Status) );
#if DBG
if (MupVerbose) {
KeQuerySystemTime(&EndTime);
DbgPrint(" [%d] DnrGetAuthenticatedConnection exit 0x%x\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
Status);
}
#endif
return( Status );
}
//+----------------------------------------------------------------------------
//
// Function: DnrReleaseAuthenticatedConnection
//
// Synopsis: Dereferences the authenticated connection we used during
// Dnr.
//
// Arguments: [DnrContext] -- The DNR_CONTEXT record for this Dnr
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
VOID
DnrReleaseAuthenticatedConnection(
IN PDNR_CONTEXT DnrContext)
{
if (DnrContext->AuthConn != NULL) {
DFS_DEREFERENCE_OBJECT( DnrContext->AuthConn );
DnrContext->AuthConn = NULL;
}
}
//+----------------------------------------------------------------------------
//
// Function: DfsBuildConnectionRequest
//
// Synopsis: Builds the file names necessary to setup an
// authenticated connection to a server's IPC$ share.
//
// Arguments: [pService] -- Pointer to DFS_SERVICE describing server
// [pProvider] -- Pointer to PROVIDER_DEF describing the
// provider to use to establish the connection.
// [pShareName] -- Share name to open.
//
// Returns: STATUS_SUCCESS or STATUS_INSUFFICIENT_RESOURCES
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsBuildConnectionRequest(
IN PDFS_SERVICE pService,
IN PPROVIDER_DEF pProvider,
OUT PUNICODE_STRING pShareName)
{
ASSERT(pService != NULL);
ASSERT(pProvider != NULL);
RtlInitUnicodeString(pShareName, NULL);
pShareName->Length = 0;
pShareName->MaximumLength = pProvider->DeviceName.Length +
sizeof(UNICODE_PATH_SEP_STR) +
pService->Name.Length +
sizeof(ROOT_SHARE_NAME);
pShareName->Buffer = ExAllocatePoolWithTag(PagedPool, pShareName->MaximumLength, ' puM');
if (pShareName->Buffer == NULL) {
DfsDbgTrace(0, Dbg, "Unable to allocate pool for share name!\n", 0);
pShareName->Length = pShareName->MaximumLength = 0;
return( STATUS_INSUFFICIENT_RESOURCES );
}
RtlAppendUnicodeStringToString( pShareName, &pProvider->DeviceName );
RtlAppendUnicodeToString( pShareName, UNICODE_PATH_SEP_STR );
RtlAppendUnicodeStringToString( pShareName, &pService->Name );
RtlAppendUnicodeToString( pShareName, ROOT_SHARE_NAME );
return( STATUS_SUCCESS );
}
//+----------------------------------------------------------------------------
//
// Function: DfsFreeConnectionRequest
//
// Synopsis: Frees up the stuff allocated on a successful call to
// DfsBuildConnectionRequest
//
// Arguments: [pShareName] -- Unicode string holding share name.
//
// Returns: Nothing
//
//-----------------------------------------------------------------------------
VOID
DfsFreeConnectionRequest(
IN OUT PUNICODE_STRING pShareName)
{
if (pShareName->Buffer != NULL) {
ExFreePool ( pShareName->Buffer );
}
}
//+-------------------------------------------------------------------------
//
// Function: DfsCreateConnection -- Create a connection to a server
//
// Synopsis: DfsCreateConnection will attempt to create a connection
// to some server's IPC$ share.
//
// Arguments: [pService] -- the Service entry, giving the server principal
// name
// [pProvider] --
//
// [CSCAgentCreate] -- TRUE if this is on behalf of CSC agent
//
// [remoteHandle] -- This is where the handle is returned.
//
// Returns: NTSTATUS - the status of the operation
//
// Notes: The Pkt must be acquired shared before calling this! It will
// be released and reacquired in this routine.
//
//--------------------------------------------------------------------------
NTSTATUS
DfsCreateConnection(
IN PDFS_SERVICE pService,
IN PPROVIDER_DEF pProvider,
IN BOOLEAN CSCAgentCreate,
OUT PHANDLE remoteHandle
) {
PFILE_FULL_EA_INFORMATION EaBuffer;
ULONG EaBufferLength;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING ShareName;
NTSTATUS Status;
BOOLEAN pktLocked;
ASSERT( PKT_LOCKED_FOR_SHARED_ACCESS() );
ASSERT(pService != NULL);
ASSERT(pProvider != NULL);
Status = DfsBuildConnectionRequest(
pService,
pProvider,
&ShareName);
if (!NT_SUCCESS(Status)) {
return( Status );
}
InitializeObjectAttributes(
&ObjectAttributes,
&ShareName, // File Name
0, // Attributes
NULL, // Root Directory
NULL // Security
);
//
// Create or open a connection to the server.
//
PktRelease();
if (CSCAgentCreate) {
EaBuffer = DfsData.CSCEaBuffer;
EaBufferLength = DfsData.CSCEaBufferLength;
} else {
EaBuffer = NULL;
EaBufferLength = 0;
}
Status = ZwCreateFile(
remoteHandle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
EaBuffer,
EaBufferLength);
MUP_TRACE_ERROR_HIGH(Status, ALL_ERROR, DfsCreateConnection_Error_ZwCreateFile,
LOGSTATUS(Status));
PktAcquireShared( TRUE, &pktLocked );
if ( NT_SUCCESS( Status ) ) {
DfsDbgTrace(0, Dbg, "Created Connection Successfully\n", 0);
Status = IoStatusBlock.Status;
}
DfsFreeConnectionRequest( &ShareName );
return Status;
}
//+-------------------------------------------------------------------------
//
// Function: DfsCloseConnection -- Close a connection to a server
//
// Synopsis: DfsCloseConnection will attempt to Close a connection
// to some server.
//
// Effects: The file object referring to the the connection will be
// closed.
//
// Arguments: [pService] - the Service entry, giving the server connection
// handle
//
// Returns: NTSTATUS - the status of the operation
//
// History: 28 May 1992 Alanw Created
//
//--------------------------------------------------------------------------
NTSTATUS
DfsCloseConnection(
IN PDFS_SERVICE pService
)
{
ASSERT( pService->ConnFile != NULL );
ObDereferenceObject(pService->ConnFile);
pService->ConnFile = NULL;
InterlockedDecrement(&pService->pMachEntry->ConnectionCount);
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------------
//
// Function: DnrBuildReferralRequest
//
// Synopsis: This routine builds all the necessary things to send
// a referral request to a DC.
//
// Arguments: [pDnrContext] -- The context for building the referral.
//
// Returns: Pointer to an IRP that can be used to get a referral.
//
//-----------------------------------------------------------------------------
PIRP
DnrBuildReferralRequest(
IN PDNR_CONTEXT DnrContext)
{
PUCHAR pNameResBuf = NULL;
PREQ_GET_DFS_REFERRAL pRef;
PWCHAR ReferralPath;
PPROVIDER_DEF pProvider;
PIRP pIrp = NULL;
ULONG cbBuffer = 0;
NTSTATUS Status;
DfsDbgTrace(+1,Dbg, "DnrBuildReferralRequest Entered - DnrContext %08lx\n", DnrContext);
cbBuffer = DnrContext->FileName.Length + sizeof(UNICODE_NULL) + sizeof(PREQ_GET_DFS_REFERRAL);
if (DnrContext->ReferralSize > cbBuffer) {
cbBuffer = DnrContext->ReferralSize;
}
else {
DnrContext->ReferralSize = cbBuffer;
}
DfsDbgTrace(0, Dbg, "Referral Size = %d bytes\n", ULongToPtr(cbBuffer));
pNameResBuf = ExAllocatePoolWithTag(NonPagedPool, cbBuffer, ' puM');
if (pNameResBuf == NULL) {
DfsDbgTrace(-1, Dbg, "Unable to allocate %d bytes\n", ULongToPtr(cbBuffer));
return( NULL );
}
pRef = (PREQ_GET_DFS_REFERRAL) pNameResBuf;
pRef->MaxReferralLevel = 3;
ReferralPath = (PWCHAR) &pRef->RequestFileName[0];
RtlMoveMemory(
ReferralPath,
DnrContext->FileName.Buffer,
DnrContext->FileName.Length);
ReferralPath[ DnrContext->FileName.Length/sizeof(WCHAR) ] = UNICODE_NULL;
ASSERT( DnrContext->DCConnFile != NULL);
#if DBG
if (MupVerbose)
DbgPrint(" DnrBuildReferrlRequest:ReferralPath=[%ws]\n", ReferralPath);
#endif
Status = DnrGetTargetInfo( DnrContext );
if (Status == STATUS_SUCCESS)
{
pIrp = DnrBuildFsControlRequest( DnrContext->DCConnFile,
DnrContext,
FSCTL_DFS_GET_REFERRALS,
pNameResBuf,
FIELD_OFFSET(REQ_GET_DFS_REFERRAL, RequestFileName) +
(wcslen(ReferralPath) + 1) * sizeof(WCHAR),
pNameResBuf,
cbBuffer,
DnrCompleteReferral );
if (pIrp == NULL) {
DfsDbgTrace(-1, Dbg, "DnrBuildReferralRequest: Unable to allocate Irp!\n", 0);
ExFreePool(pNameResBuf);
} else {
DfsDbgTrace(-1, Dbg, "DnrBuildReferralRequest: returning %08lx\n", pIrp);
}
}
return( pIrp );
}
//+----------------------------------------------------------------------------
//
// Function: DnrInsertReferralAndResume
//
// Synopsis: This routine is queued as a work item from DnrComplete
// referral and does the work of actually inserting the
// referral and resuming DNR. We must not do this work
// directly in DnrCompleteReferral because it operates at
// raised IRQL.
//
// Arguments:
//
// Returns:
//
//-----------------------------------------------------------------------------
VOID
DnrInsertReferralAndResume(
IN PVOID Context)
{
PIRP_CONTEXT pIrpContext = (PIRP_CONTEXT) Context;
PDNR_CONTEXT DnrContext = (PDNR_CONTEXT) pIrpContext->Context;
PIRP Irp = pIrpContext->OriginatingIrp;
PRESP_GET_DFS_REFERRAL pRefResponse;
ULONG length, matchingLength;
NTSTATUS status;
LARGE_INTEGER EndTime;
ULONG referralType = 0;
DfsDbgTrace(+1, Dbg, "DnrInsertReferralAndResume: Entered\n", 0);
DfsDbgTrace(0, Dbg, "Irp = %x\n", Irp );
DfsDbgTrace(0, Dbg, "Context = %x\n", Context);
ASSERT(DnrContext->State == DnrStateCompleteReferral);
status = Irp->IoStatus.Status;
length = (ULONG)Irp->IoStatus.Information;
DfsDbgTrace(0, Dbg, "Irp->Status = %x\n", ULongToPtr(status) );
DfsDbgTrace(0, Dbg, "Irp->Length = %x\n", ULongToPtr(length) );
KeQuerySystemTime(&EndTime);
#if DBG
if (MupVerbose)
DbgPrint(" [%d] DnrInsertReferralAndResume entered for [%wZ] status = 0x%x\n",
(ULONG)((EndTime.QuadPart - DnrContext->StartTime.QuadPart)/(10 * 1000)),
&DnrContext->FileName,
status);
#endif
//
// If the DC returned STATUS_BUFFER_OVERFLOW, the referral didn't fit in
// the buffer we sent. Increase the buffer and retry the referral request
// Since we are going to retry the request, we won't dereference the
// provider's device object yet.
//
if (status == STATUS_BUFFER_OVERFLOW) {
PULONG pcbSize;
if (DnrContext->ReferralSize < MAX_REFERRAL_MAX) {
DfsDbgTrace(0, Dbg, "Referral buffer was too small; retrying...\n", 0);
DnrContext->ReferralSize = MAX_REFERRAL_MAX;
DnrContext->State = DnrStateGetReferrals;
//
// Going back into Dnr. Reacquire the Pkt shared, and release the
// PktReferralRequests semaphore, so we can go out again to get a
// referral.
//
goto Cleanup;
}
}
//
// If we got an error and we were using a cached IPC$ connection,
// close the cached IPC$ and retry the referral request.
//
if ( (DnrContext->CachedConnFile == TRUE) &&
(status != STATUS_SUCCESS) )
{
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
ExAcquireResourceExclusiveLite(&DfsData.Resource, TRUE);
DnrContext->CachedConnFile = FALSE;
if (DnrContext->pService->ConnFile != NULL) {
DfsCloseConnection(DnrContext->pService);
}
DnrContext->State = DnrStateGetReferrals;
ExReleaseResourceLite( &DfsData.Resource );
PktConvertExclusiveToShared();
//
// Going back into Dnr. Reacquire the Pkt shared, and release the
// PktReferralRequests semaphore, so we can go out again to get a
// referral.
//
goto Cleanup;
}
//
// The referral request has terminated. Since we are done with the
// provider, dereference its device object now.
//
DFS_DEREFERENCE_OBJECT(DnrContext->TargetDevice);
//
// Next, handle the result of the Referral request. If we successfully
// got a referral, then try to insert it into our Pkt.
//
if (NT_SUCCESS(status) && length != 0) {
pRefResponse = (PRESP_GET_DFS_REFERRAL) Irp->AssociatedIrp.SystemBuffer;
DfsDbgTrace(0, Dbg, "Irp->Buffer = %x\n", pRefResponse );
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
status = PktCreateEntryFromReferral(
&DfsData.Pkt,
&DnrContext->FileName,
length,
pRefResponse,
PKT_ENTRY_SUPERSEDE,
DnrContext->pNewTargetInfo,
&matchingLength,
&referralType,
&DnrContext->pPktEntry);
DNR_SET_TARGET_INFO( DnrContext, DnrContext->pPktEntry );
if (status == STATUS_INVALID_USER_BUFFER) {
status = STATUS_BAD_NETWORK_NAME;
}
if (NT_SUCCESS(status) && DfsEventLog > 1) {
UNICODE_STRING puStr[2];
puStr[0] = DnrContext->FileName;
puStr[1] = DnrContext->pService->Name;
LogWriteMessage(DFS_REFERRAL_SUCCESS, status, 2, puStr);
}
if (NT_SUCCESS(status)) {
UNICODE_STRING fileName;
UNICODE_STRING RemPath;
PDFS_PKT_ENTRY pEntry = NULL;
PDFS_PKT_ENTRY pShortPfxEntry = NULL;
PDFS_PKT Pkt;
//
// See if we have to remove an entry
//
pEntry = PktLookupEntryByPrefix(
&DfsData.Pkt,
&DnrContext->FileName,
&RemPath);
pShortPfxEntry = PktLookupEntryByShortPrefix(
&DfsData.Pkt,
&DnrContext->FileName,
&RemPath);
if (pShortPfxEntry != NULL) {
if ((pEntry == NULL) ||
(pShortPfxEntry->Id.Prefix.Length > pEntry->Id.Prefix.Length)) {
pEntry = pShortPfxEntry;
}
}
if (pEntry != NULL && pEntry != DnrContext->pPktEntry) {
#if DBG
if (MupVerbose)
DbgPrint(" DnrInsertReferralAndResume: Need to remove pEntry [%wZ]\n",
&pEntry->Id.Prefix);
#endif
Pkt = _GetPkt();
if ((pEntry->Type & PKT_ENTRY_TYPE_PERMANENT) == 0) {
PktFlushChildren(pEntry);
if (pEntry->UseCount == 0) {
PktEntryDestroy(pEntry, Pkt, (BOOLEAN) TRUE);
} else {
pEntry->Type |= PKT_ENTRY_TYPE_DELETE_PENDING;
pEntry->ExpireTime = 0;
DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(pEntry->Id.Prefix));
DfsRemoveUnicodePrefix(&Pkt->ShortPrefixTable, &(pEntry->Id.ShortPrefix));
}
}
}
//
// At this point, we are essentially in the same state as we were
// when we started DNR, except that we have one more Pkt entry to
// match against. Continue with name resolution, after fixing up
// DnrContext->RemainingPart to reflect the match with the new
// PktEntry.
//
fileName = DnrContext->FileName;
DnrContext->RemainingPart.Length =
(USHORT) (fileName.Length - matchingLength);
DnrContext->RemainingPart.MaximumLength =
(USHORT) (fileName.MaximumLength - matchingLength);
DnrContext->RemainingPart.Buffer =
&fileName.Buffer[ matchingLength/sizeof(WCHAR) ];
DnrContext->GotReferral = TRUE;
DnrContext->State = DnrStateStart;
} else {
DnrContext->FinalStatus = status;
DnrContext->State = DnrStateDone;
}
PktConvertExclusiveToShared();
} else if (status == STATUS_NO_SUCH_DEVICE) {
UNICODE_STRING RemPath;
PDFS_PKT_ENTRY pEntry = NULL;
PDFS_PKT Pkt;
BOOLEAN pktLocked;
//
// Check if there is a pkt entry (probably stale) that needs to be removed
//
#if DBG
if (MupVerbose)
DbgPrint(" DnrInsertReferralAndResume: remove PKT entry for \\%wZ\n",
&DnrContext->FileName);
#endif
PktAcquireExclusive( TRUE, &DnrContext->ReleasePkt );
Pkt = _GetPkt();
#if DBG
if (MupVerbose)
DbgPrint(" Looking up %wZ\n", &DnrContext->FileName);
#endif
pEntry = PktLookupEntryByPrefix(
&DfsData.Pkt,
&DnrContext->FileName,
&RemPath);
#if DBG
if (MupVerbose)
DbgPrint(" pEntry=0x%x\n", pEntry);
#endif
if (pEntry != NULL && (pEntry->Type & PKT_ENTRY_TYPE_PERMANENT) == 0) {
PktFlushChildren(pEntry);
if (pEntry->UseCount == 0) {
PktEntryDestroy(pEntry, Pkt, (BOOLEAN) TRUE);
} else {
pEntry->Type |= PKT_ENTRY_TYPE_DELETE_PENDING;
pEntry->ExpireTime = 0;
DfsRemoveUnicodePrefix(&Pkt->PrefixTable, &(pEntry->Id.Prefix));
DfsRemoveUnicodePrefix(&Pkt->ShortPrefixTable, &(pEntry->Id.ShortPrefix));
}
}
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
DnrContext->FinalStatus = status;
DnrContext->State = DnrStateDone;
PktConvertExclusiveToShared();
} else if (ReplIsRecoverableError(status)) {
DnrContext->State = DnrStateGetNextDC;
} else if (status == STATUS_BUFFER_OVERFLOW) {
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
DnrContext->FinalStatus = status;
DnrContext->State = DnrStateDone;
} else if (DnrContext->Attempts > 0) {
#if DBG
if (MupVerbose)
DbgPrint(" DnrInsertReferralAndResume restarting Attempts = %d\n",
DnrContext->Attempts);
#endif
DnrContext->State = DnrStateStart;
} else {
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
DnrContext->FinalStatus = status;
DnrContext->State = DnrStateDone;
}
if ( !NT_SUCCESS(status) && DfsEventLog > 0) {
UNICODE_STRING puStr[2];
if (!DnrContext->ReleasePkt)
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
if (DnrContext->USN == DnrContext->pPktEntry->USN) {
puStr[0] = DnrContext->FileName;
puStr[1] = DnrContext->pService->Name;
LogWriteMessage(DFS_REFERRAL_FAILURE, status, 2, puStr);
}
}
Cleanup:
//
// Cleanup referral stuff.
//
if (Irp->UserBuffer && Irp->UserBuffer != Irp->AssociatedIrp.SystemBuffer)
ExFreePool( Irp->UserBuffer );
if (Irp->AssociatedIrp.SystemBuffer) {
ExFreePool( Irp->AssociatedIrp.SystemBuffer );
}
IoFreeIrp(Irp);
PktReleaseTargetInfo( DnrContext->pNewTargetInfo );
DnrContext->pNewTargetInfo = NULL;
DfsDeleteIrpContext(pIrpContext);
//
// We are going back into Dnr, so prepare for that:
//
// - Reacquire PKT shared
// - Release the semaphore for referral requests, so that the next
// thread can get its referral.
// - Restart Dnr
//
if (!DnrContext->ReleasePkt)
PktAcquireShared( TRUE, &DnrContext->ReleasePkt );
ASSERT(DnrContext->ReleasePkt == TRUE);
#if DBG
if (MupVerbose)
DbgPrint(" DnrInsertReferralAndResume next State=%d\n", DnrContext->State);
#endif
DnrContext->Impersonate = TRUE;
DnrContext->DnrActive = FALSE;
DnrNameResolve(DnrContext);
PsAssignImpersonationToken(PsGetCurrentThread(),NULL);
DfsDbgTrace(-1, Dbg, "DnrInsertReferralAndResume: Exit -> %x\n", ULongToPtr(status) );
}
//
// The following two functions are DPC functions which participate
// in the Distributed Name Resolution process. Each takes a
// PDNR_CONTEXT as input, transitions the state of the
// name resolution and any associated data structures, and
// invokes the next step of the process.
//
//+-------------------------------------------------------------------
//
// Function: DnrCompleteReferral, local
//
// Synopsis: This is the completion routine for processing a referral
// response. Cleanup the IRP and continue processing the name
// resolution request.
//
// Arguments: [pDevice] -- Pointer to target device object for
// the request.
// [Irp] -- Pointer to I/O request packet
// [Context] -- Caller-specified context parameter associated
// with IRP. This is actually a pointer to a DNR
// Context block.
//
// Returns: [STATUS_MORE_PROCESSING_REQUIRED] -- The referral Irp was
// constructed by Dnr and will be freed by us. So, we
// don't want the IO Subsystem to touch the Irp anymore.
//
// Notes: This routine executes at DPC level. We should do an absolutely
// minimum amount of work here.
//
//--------------------------------------------------------------------
NTSTATUS
DnrCompleteReferral(
IN PDEVICE_OBJECT pDevice,
IN PIRP Irp,
IN PVOID Context
) {
PIRP_CONTEXT pIrpContext = NULL;
PDNR_CONTEXT DnrContext = (PDNR_CONTEXT) Context;
DfsDbgTrace(+1, Dbg, "DnrCompleteReferral: Entered\n", 0);
DfsDbgTrace(0, Dbg, "Irp = %x\n", Irp);
DfsDbgTrace(0, Dbg, "Context = %x\n", Context);
//
// Derefernce the file object over which we sent the referral request
//
DFS_DEREFERENCE_OBJECT( DnrContext->DCConnFile );
DnrContext->DCConnFile = NULL;
//
// Release the semaphore controlling number of referral requests
//
KeReleaseSemaphore(
&DfsData.PktReferralRequests,
0, // Priority boost
1, // Increment semaphore amount
FALSE); // Won't call wait immediately
try {
pIrpContext = DfsCreateIrpContext(Irp, TRUE);
if (pIrpContext == NULL)
ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
pIrpContext->Context = DnrContext;
ExInitializeWorkItem(
&pIrpContext->WorkQueueItem,
DnrInsertReferralAndResume,
pIrpContext);
ExQueueWorkItem( &pIrpContext->WorkQueueItem, CriticalWorkQueue );
} except(DfsExceptionFilter(pIrpContext, GetExceptionCode(), GetExceptionInformation())) {
//
// Ok, we can't queue the work item and complete Dnr. So, we have
// to do two things. First of all, clean up the current Irp (ie,
// the Referral Irp), then complete the original Dnr Irp
//
//
// Cleanup referral stuff.
//
if (Irp->UserBuffer && Irp->UserBuffer != Irp->AssociatedIrp.SystemBuffer)
ExFreePool( Irp->UserBuffer );
if (Irp->AssociatedIrp.SystemBuffer) {
ExFreePool( Irp->AssociatedIrp.SystemBuffer );
}
IoFreeIrp(Irp);
PktReleaseTargetInfo( DnrContext->pNewTargetInfo );
DnrContext->pNewTargetInfo = NULL;
if (pIrpContext) {
//
// Maybe this should be an assert that pIrpContext == NULL. If
// it is not NULL, then the Irp context was allocated, so who
// threw an exception anyway?
//
DfsDeleteIrpContext(pIrpContext);
}
//
// Now, call Dnr to complete the original Irp
//
InterlockedDecrement(&DnrContext->pPktEntry->UseCount);
DnrContext->DnrActive = FALSE;
DnrContext->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
DnrContext->State = DnrStateDone;
DnrNameResolve(DnrContext);
PsAssignImpersonationToken(PsGetCurrentThread(),NULL);
}
//
// Return more processing required to the IO system so that it
// doesn't attempt further processing on the IRP. The IRP will be
// freed by DnrInsertReferralAndResume, or has already been freed
// if we couldn't queue up DnrInsertReferralAndResume
//
DfsDbgTrace(-1, Dbg, "DnrCompleteReferral: Exit -> %x\n",
ULongToPtr(STATUS_MORE_PROCESSING_REQUIRED) );
UNREFERENCED_PARAMETER(pDevice);
return STATUS_MORE_PROCESSING_REQUIRED;
}
//+-------------------------------------------------------------------
//
// Function: DnrCompleteFileOpen, local
//
// Synopsis: This is the completion routine for processing a file open
// request. Cleanup the IRP and continue processing the name
// resolution request.
//
// Arguments: [pDevice] -- Pointer to target device object for
// the request.
// [Irp] -- Pointer to I/O request packet
// [Context] -- Caller-specified context parameter associated
// with IRP. This is actually a pointer to a DNR
// Context block.
//
// Returns: [STATUS_MORE_PROCESSING_REQUIRED] -- We still have to finish
// the DNR, so we halt further completion of this Irp
// by returning STATUS_MORE_PROCESSING_REQUIRED.
//
//--------------------------------------------------------------------
NTSTATUS
DnrCompleteFileOpen(
IN PDEVICE_OBJECT pDevice,
IN PIRP Irp,
IN PVOID Context
) {
PDNR_CONTEXT DnrContext;
NTSTATUS status;
PFILE_OBJECT FileObject;
PIO_STACK_LOCATION IrpSp;
DfsDbgTrace(+1, Dbg, "DnrCompleteFileOpen: Entered\n", 0);
DfsDbgTrace(0, Dbg, "Irp = %x\n", Irp );
DfsDbgTrace(0, Dbg, "Context = %x\n", Context);
DnrContext = Context;
status = Irp->IoStatus.Status;
DfsDbgTrace(0, Dbg, "Irp->Status = %x\n", ULongToPtr(status) );
DnrContext->FinalStatus = status;
DnrReleaseAuthenticatedConnection( DnrContext );
DFS_DEREFERENCE_OBJECT( DnrContext->TargetDevice );
IrpSp = IoGetCurrentIrpStackLocation(Irp);
FileObject = IrpSp->FileObject;
//
// If STATUS_PENDING was initially returned for the IRP, then we need
// to restart DNR. So, we post a workitem into the FSP, giving it the
// DnrContext to resume the DNR.
//
// If STATUS_PENDING was not returned, then we simply stop the
// unwinding of the IRP stack by returning STATUS_MORE_PROCESSING_REQUIRED
// IoCallDriver will eventually return to DnrRedirectFileOpen, which
// will continue with the DNR.
//
if (Irp->PendingReturned) {
//
// Schedule a work item to resume DNR
//
DnrContext->Impersonate = TRUE;
DnrContext->DnrActive = FALSE;
DnrContext->State = DnrStatePostProcessOpen;
ASSERT(DnrContext->pIrpContext->MajorFunction == IRP_MJ_CREATE);
DnrContext->pIrpContext->Context = (PVOID) DnrContext;
//
// We need to call IpMarkIrpPending so the IoSubsystem will realize
// that our FSD routine returned STATUS_PENDING. We can't call this
// from the FSD routine itself because the FSD routine doesn't have
// access to the stack location when the underlying guy returns
// STATUS_PENDING
//
IoMarkIrpPending( Irp );
DfsFsdPostRequest(DnrContext->pIrpContext, DnrContext->OriginalIrp);
}
//
// Return more processing required to the IO system so that it
// stops unwinding the completion routine stack. The DNR that will
// be resumed will call IoCompleteRequest when appropriate
// and resume the unwinding of the completion routine stack.
//
status = STATUS_MORE_PROCESSING_REQUIRED;
DfsDbgTrace(-1, Dbg, "DnrCompleteFileOpen: Exit -> %x\n", ULongToPtr(status) );
UNREFERENCED_PARAMETER(pDevice);
return status;
}
//+-------------------------------------------------------------------
//
// Function: DnrBuildFsControlRequest, local
//
// Synopsis: This function builds an I/O request packet for a device or
// file system I/O control request.
//
// Arguments: [FileObject] -- Supplies a pointer the file object to which this
// request is directed. This pointer is copied into the
// IRP, so that the called driver can find its file-based
// context. NOTE THAT THIS IS NOT A REFERENCED POINTER.
// The caller must ensure that the file object is not
// deleted while the I/O operation is in progress. The
// server accomplishes this by incrementing a reference
// count in a local block to account for the I/O; the
// local block in turn references the file object.
// [Context] -- Supplies a PVOID value that is passed to the
// completion routine.
// [FsControlCode] -- Supplies the control code for the operation.
// [MainBuffer] -- Supplies the address of the main buffer. This
// must be a system virtual address, and the buffer must
// be locked in memory. If ControlCode specifies a method
// 0 request, the actual length of the buffer must be the
// greater of InputBufferLength and OutputBufferLength.
// [InputBufferLength] -- Supplies the length of the input buffer.
// [AuxiliaryBuffer] -- Supplies the address of the auxiliary
// buffer. If the control code method is 0, this is a
// buffered I/O buffer, but the data returned by the
// called driver in the system buffer is not
// automatically copied into the auxiliary buffer.
// Instead, the auxiliary data ends up in MainBuffer.
// If the caller wishes the data to be in AuxiliaryBuffer,
// it must copy the data at some point after the
// completion routine runs.
// [CompletionRoutine] -- The IO completion routine.
//
// Returns: PIRP -- Returns a pointer to the constructed IRP. If the Irp
// parameter was not NULL on input, the function return value will
// be the same value (so it is safe to discard the return value in
// this case). It is the responsibility of the calling program to
// deallocate the IRP after the I/O request is complete.
//
// Notes: should we use IoBuildIoControlRequest instead?
//
//--------------------------------------------------------------------
PIRP
DnrBuildFsControlRequest (
IN PFILE_OBJECT FileObject OPTIONAL,
IN PVOID Context,
IN ULONG IoControlCode,
IN PVOID MainBuffer,
IN ULONG InputBufferLength,
IN PVOID AuxiliaryBuffer OPTIONAL,
IN ULONG OutputBufferLength,
IN PIO_COMPLETION_ROUTINE CompletionRoutine
) {
CLONG method;
PDEVICE_OBJECT deviceObject;
PIRP Irp;
PIO_STACK_LOCATION irpSp;
const UCHAR MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL;
ASSERT( MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL );
//
// Get the method with which the buffers are being passed.
//
method = IoControlCode & 3;
ASSERT( method == METHOD_BUFFERED );
//
// Allocate an IRP. The stack size is one higher
// than that of the target device, to allow for the caller's
// completion routine.
//
deviceObject = IoGetRelatedDeviceObject( FileObject );
//
// Get the address of the target device object.
//
Irp = IoAllocateIrp( (CCHAR)(deviceObject->StackSize + 1), FALSE );
if ( Irp == NULL ) {
//
// Unable to allocate an IRP. Inform the caller.
//
return NULL;
}
Irp->RequestorMode = KernelMode;
Irp->Tail.Overlay.OriginalFileObject = FileObject;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
//
// Get a pointer to the current stack location and fill in the
// device object pointer.
//
IoSetNextIrpStackLocation( Irp );
irpSp = IoGetCurrentIrpStackLocation( Irp );
irpSp->DeviceObject = deviceObject;
//
// Set up the completion routine.
//
IoSetCompletionRoutine(
Irp,
CompletionRoutine,
Context,
TRUE,
TRUE,
TRUE
);
//
// Get a pointer to the next stack location. This one is used to
// hold the parameters for the device I/O control request.
//
irpSp = IoGetNextIrpStackLocation( Irp );
irpSp->MajorFunction = MajorFunction;
irpSp->MinorFunction = 0;
irpSp->FileObject = FileObject;
irpSp->DeviceObject = deviceObject;
//
// Copy the caller's parameters to the service-specific portion of the
// IRP for those parameters that are the same for all three methods.
//
irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
Irp->MdlAddress = NULL;
Irp->AssociatedIrp.SystemBuffer = MainBuffer;
Irp->UserBuffer = AuxiliaryBuffer;
Irp->Flags = (ULONG)IRP_BUFFERED_IO;
if ( ARGUMENT_PRESENT(AuxiliaryBuffer) ) {
Irp->Flags |= IRP_INPUT_OPERATION;
}
return Irp;
}
//+-------------------------------------------------------------------------
//
// Function: AllocateDnrContext, public
//
// Synopsis: AllocateDnrContext will allocate a DNR_CONTEXT
// record.
//
// Arguments: -none-
//
// Returns: PDNR_CONTEXT - a pointer to the allocated DNR_CONTEXT;
// NULL if not enough memory.
//
// Notes: We should investigate allocating this out of the
// IrpContext Zone if they are similar enough in size.
//
//--------------------------------------------------------------------------
PDNR_CONTEXT
AllocateDnrContext(
IN ULONG cbExtra
) {
PDNR_CONTEXT pNRC;
pNRC = ExAllocatePoolWithTag( NonPagedPool, sizeof (DNR_CONTEXT) + cbExtra, ' puM');
if (pNRC == NULL) {
return NULL;
}
RtlZeroMemory(pNRC, sizeof (DNR_CONTEXT));
pNRC->NodeTypeCode = DSFS_NTC_DNR_CONTEXT;
pNRC->NodeByteSize = sizeof (DNR_CONTEXT);
return pNRC;
}
//+-------------------------------------------------------------------------
//
// Function: DnrConcatenateFilePath, public
//
// Synopsis: DnrConcatenateFilePath will concatenate two strings
// representing file path names, assuring that they are
// separated by a single '\' character.
//
// Arguments: [Dest] - a pointer to the destination string
// [RemainingPath] - the final part of the path name
// [Length] - the length (in bytes) of RemainingPath
//
// Returns: BOOLEAN - TRUE unless Dest is too small to
// hold the result (assert).
//
//--------------------------------------------------------------------------
BOOLEAN
DnrConcatenateFilePath (
IN PUNICODE_STRING Dest,
IN PWSTR RemainingPath,
IN USHORT Length
) {
PWSTR OutBuf = (PWSTR)&(((PCHAR)Dest->Buffer)[Dest->Length]);
ULONG NewLen;
if (Dest->Length > 0) {
ASSERT(OutBuf[-1] != UNICODE_NULL);
}
if (Length == 0)
{
return TRUE;
}
if (Dest->Length > 0 && OutBuf[-1] != UNICODE_PATH_SEP) {
*OutBuf++ = UNICODE_PATH_SEP;
Dest->Length += sizeof (WCHAR);
}
if (Length > 0 && *RemainingPath == UNICODE_PATH_SEP) {
RemainingPath++;
Length -= sizeof (WCHAR);
}
NewLen = (ULONG)Dest->Length + (ULONG)Length;
if (NewLen > MAXUSHORT || NewLen > (ULONG)Dest->MaximumLength) {
return FALSE;
}
if (Length > 0) {
RtlMoveMemory(OutBuf, RemainingPath, Length);
Dest->Length += Length;
}
return TRUE;
}
//+----------------------------------------------------------------------------
//
// Function: DnrLocateDC, private
//
// Synopsis: Tries to create an entry for the Dfs root which will match
// FileName.
//
// Arguments: [FileName] -- File name for which DC/Root is being located.
//
// Returns: Nothing.
//
//-----------------------------------------------------------------------------
VOID
DnrLocateDC(
IN PUNICODE_STRING FileName)
{
BOOLEAN pktLocked;
NTSTATUS status;
UNICODE_STRING dfsRoot, dfsShare, remPath;
UNICODE_STRING RootShareName;
PDFS_PKT_ENTRY pktEntry;
PDFS_PKT pkt;
USHORT i, j;
ASSERT( FileName->Buffer[0] == UNICODE_PATH_SEP );
#if DBG
if (MupVerbose)
DbgPrint(" DnrLocateDC(%wZ)\n", FileName);
#endif
dfsRoot.Length = dfsRoot.MaximumLength = 0;
dfsRoot.Buffer = &FileName->Buffer[1];
for (i = 1;
i < FileName->Length/sizeof(WCHAR) &&
FileName->Buffer[i] != UNICODE_PATH_SEP;
i++) {
NOTHING;
}
for (j = i + 1;
j < FileName->Length/sizeof(WCHAR) &&
FileName->Buffer[j] != UNICODE_PATH_SEP;
j++) {
NOTHING;
}
if ((FileName->Buffer[i] == UNICODE_PATH_SEP) && (j > i)) {
dfsRoot.MaximumLength = dfsRoot.Length = (i - 1) * sizeof(WCHAR);
dfsShare.MaximumLength = dfsShare.Length = (j - i - 1) * sizeof(WCHAR);
dfsShare.Buffer = &FileName->Buffer[i+1];
#if DBG
if (MupVerbose)
DbgPrint(" DnrLocateDC dfsRoot=[%wZ] dfsShare=[%wZ]\n",
&dfsRoot,
&dfsShare);
#endif
status = PktCreateDomainEntry( &dfsRoot, &dfsShare, FALSE );
if (!NT_SUCCESS(status)) {
#if DBG
if (MupVerbose)
DbgPrint(" DnrLocateDC:PktCreateDomainEntry() returned 0x%x\n", status);
#endif
RootShareName.Buffer = FileName->Buffer;
RootShareName.Length = j * sizeof(WCHAR);
RootShareName.MaximumLength = FileName->MaximumLength;
#if DBG
if (MupVerbose)
DbgPrint(" DnrLocateDC:RootShareName=[%wZ]\n", &RootShareName);
#endif
//
// Failed getting referral - see if we have a stale one.
//
pkt = _GetPkt();
PktAcquireShared( TRUE, &pktLocked );
pktEntry = PktLookupEntryByPrefix( pkt, &RootShareName, &remPath );
if (pktEntry != NULL) {
#if DBG
if (MupVerbose)
DbgPrint(" DnrLocateDC:Found stale pkt entry %08lx - adding 60 sec to it\n",
pktEntry);
#endif
if (pktEntry->ExpireTime <= 0) {
pktEntry->ExpireTime = 60;
pktEntry->TimeToLive = 60;
}
status = STATUS_SUCCESS;
}
PktRelease();
}
} else {
status = STATUS_BAD_NETWORK_PATH;
}
#if DBG
if (MupVerbose)
DbgPrint(" DnrLocateDC exit 0x%x\n", status);
#endif
}
//+----------------------------------------------------------------------------
//
// Function: DnrRebuildDnrContext
//
// Synopsis: To handle inter-dfs links, we simply want to change the name
// of the file we are opening to the name in the new Dfs, and
// restart DNR afresh.
//
// This is most easily done by simply terminating this DNR with
// STATUS_REPARSE. However, if we do that, we would loose track
// of the credentials (if any) we originally came in with.
//
// Hence, this routine simply rebuilds the DnrContext. After
// calling this, Dnr starts all over again, just as if
// DnrNameResolve had been called by DnrStartNameResolution
//
//
// Arguments: [DnrContext] -- The context to rebuild.
// [NewDfsPrefix] -- The prefix of the Dfs in which the DNR
// should continue.
// [RemainingPath] -- Path relative to the NewDfsPrefix.
//
// Returns: Nothing.
//
//-----------------------------------------------------------------------------
VOID
DnrRebuildDnrContext(
IN PDNR_CONTEXT DnrContext,
IN PUNICODE_STRING NewDfsPrefix,
IN PUNICODE_STRING RemainingPath)
{
UNICODE_STRING newFileName;
ULONG newNameLen = 0;
PIRP Irp = DnrContext->OriginalIrp;
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
PFILE_OBJECT FileObject = IrpSp->FileObject;
//
// Build the new file name
//
newFileName.Length = 0;
newNameLen = NewDfsPrefix->Length +
sizeof(UNICODE_PATH_SEP) +
RemainingPath->Length +
sizeof(UNICODE_NULL);
if (newNameLen >= MAXUSHORT) {
DnrContext->FinalStatus = STATUS_NAME_TOO_LONG;
DnrContext->State = DnrStateDone;
return;
}
newFileName.MaximumLength = (USHORT)newNameLen;
newFileName.Buffer = (PWCHAR) ExAllocatePoolWithTag(
NonPagedPool,
newFileName.MaximumLength,
' puM');
if (newFileName.Buffer != NULL) {
newFileName.Length = NewDfsPrefix->Length;
RtlMoveMemory(
newFileName.Buffer,
NewDfsPrefix->Buffer,
newFileName.Length);
DnrConcatenateFilePath(
&newFileName,
RemainingPath->Buffer,
RemainingPath->Length);
if (DnrContext->NameAllocated)
ExFreePool(DnrContext->FileName.Buffer);
DnrContext->NameAllocated = TRUE;
DnrContext->FileName = newFileName;
} else {
DnrContext->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
DnrContext->State = DnrStateDone;
return;
}
//
// Rebuild the FcbToUse because the Fcb has room for the full file name
// and it might have changed its size.
//
ASSERT(DnrContext->FcbToUse != NULL);
DfsDetachFcb(DnrContext->FcbToUse->FileObject, DnrContext->FcbToUse);
ExFreePool(DnrContext->FcbToUse);
DnrContext->FcbToUse = DfsCreateFcb(
DnrContext->pIrpContext,
DnrContext->Vcb,
&DnrContext->ContextFileName);
if (DnrContext->FcbToUse == NULL) {
DnrContext->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
DnrContext->State = DnrStateDone;
return;
}
DnrContext->FcbToUse->FileObject = FileObject;
DfsSetFileObject(FileObject,
RedirectedFileOpen,
DnrContext->FcbToUse);
//
// Now, whack the rest of the DnrContext. Clean it up so it is essentially
// zeroed out..
//
DnrContext->State = DnrStateStart;
DnrContext->pPktEntry = NULL;
DnrContext->USN = 0;
DnrContext->pService = NULL;
DnrContext->pProvider = NULL;
DnrContext->ProviderId = 0;
DnrContext->TargetDevice = NULL;
DnrContext->AuthConn = NULL;
DnrContext->DCConnFile = NULL;
DnrContext->ReferralSize = MAX_REFERRAL_LENGTH;
DnrContext->Attempts++;
DnrContext->GotReferral = FALSE;
DnrContext->FoundInconsistency = FALSE;
DnrContext->CalledDCLocator = FALSE;
DnrContext->CachedConnFile = FALSE;
DnrContext->DfsNameContext.Flags = 0;
DnrContext->GotReparse = FALSE;
}
//+-------------------------------------------------------------------------
//
// Function: DfspGetOfflineEntry -- Checks for a server marked offline.
//
// Synopsis: DfspGetOfflineEntry returns an offline entry for the given
// server, if it exists in our offline database.
//
// Arguments: [servername] - Name of the server of interest.
// DfsData.Resource assumed held on entry.
//
// Returns: PLIST_ENTRY: plist_entry of the offline structure.
//
//--------------------------------------------------------------------------
PLIST_ENTRY
DfspGetOfflineEntry(
PUNICODE_STRING ServerName)
{
PLIST_ENTRY listEntry;
PDFS_OFFLINE_SERVER server;
listEntry = DfsData.OfflineRoots.Flink;
while ( listEntry != &DfsData.OfflineRoots ) {
server = CONTAINING_RECORD(
listEntry,
DFS_OFFLINE_SERVER,
ListEntry);
if (RtlCompareUnicodeString(
&server->LogicalServerName, ServerName, TRUE) == 0) {
break;
}
listEntry = listEntry->Flink;
}
if (listEntry == &DfsData.OfflineRoots) {
listEntry = NULL;
}
return listEntry;
}
//+-------------------------------------------------------------------------
//
// Function: DfspMarkServerOnline -- Marks a server online
//
// Synopsis: DfspMarkServerOnline removes the server entry from its offline
// database, if it was marked as offline earlier.
//
// Arguments: [servername] - Name of the server of interest.
//
// Returns: NTSTATUS.
//
//--------------------------------------------------------------------------
NTSTATUS
DfspMarkServerOnline(
PUNICODE_STRING ServerName)
{
PLIST_ENTRY listEntry;
PDFS_OFFLINE_SERVER server;
NTSTATUS status;
UNICODE_STRING dfsRootName;
ULONG i;
DfsDbgTrace(+1, Dbg, "DfspMarkServerOnline: %wZ\n", ServerName);
//
// Extract server name when there is \server\share
//
dfsRootName = *ServerName;
if (dfsRootName.Buffer[0] == UNICODE_PATH_SEP) {
for (i = 1;
i < dfsRootName.Length/sizeof(WCHAR) &&
dfsRootName.Buffer[i] != UNICODE_PATH_SEP;
i++) {
NOTHING;
}
dfsRootName.Length = (USHORT) ((i-1) * sizeof(WCHAR));
dfsRootName.MaximumLength = dfsRootName.Length;
dfsRootName.Buffer = &ServerName->Buffer[1];
}
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
listEntry = DfspGetOfflineEntry(&dfsRootName);
if (listEntry != NULL) {
RemoveEntryList(listEntry);
}
ExReleaseResourceLite( &DfsData.Resource );
if (listEntry != NULL) {
server = CONTAINING_RECORD(
listEntry,
DFS_OFFLINE_SERVER,
ListEntry);
ExFreePool(server->LogicalServerName.Buffer);
ExFreePool(server);
}
MupInvalidatePrefixTable();
status = STATUS_SUCCESS;
DfsDbgTrace(-1, Dbg, "DfspMarkServerOnline: status %x\n", ULongToPtr(status));
return status;
}
//+-------------------------------------------------------------------------
//
// Function: DfspMarkServerOffline -- Marks a server offline
//
// Synopsis: DfspMarkServerOffline adds the server entry to its offline
// database.
//
// Arguments: [servername] - Name of the server of interest.
//
// Returns: NTSTATUS.
//
//--------------------------------------------------------------------------
NTSTATUS
DfspMarkServerOffline(
PUNICODE_STRING ServerName)
{
UNICODE_STRING NewName;
PLIST_ENTRY listEntry;
PDFS_OFFLINE_SERVER server;
UNICODE_STRING dfsRootName;
ULONG i;
DfsDbgTrace(+1, Dbg, "DfspMarkServerOffline: %wZ \n", ServerName);
server = ExAllocatePoolWithTag( PagedPool, sizeof(DFS_OFFLINE_SERVER), ' puM' );
if (server == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Extract server name when there is \server\share
//
dfsRootName = *ServerName;
if (dfsRootName.Buffer[0] == UNICODE_PATH_SEP) {
for (i = 1;
i < dfsRootName.Length/sizeof(WCHAR) &&
dfsRootName.Buffer[i] != UNICODE_PATH_SEP;
i++) {
NOTHING;
}
dfsRootName.Length = (USHORT) ((i-1) * sizeof(WCHAR));
dfsRootName.MaximumLength = dfsRootName.Length;
dfsRootName.Buffer = &ServerName->Buffer[1];
}
NewName.Buffer = ExAllocatePoolWithTag(
PagedPool,
dfsRootName.Length + sizeof(WCHAR),
' puM' );
if (NewName.Buffer == NULL) {
ExFreePool(server);
return STATUS_INSUFFICIENT_RESOURCES;
}
NewName.MaximumLength = dfsRootName.Length + sizeof(WCHAR);
NewName.Length = 0;
RtlCopyUnicodeString(&NewName, &dfsRootName);
server->LogicalServerName = NewName;
ExAcquireResourceExclusiveLite( &DfsData.Resource, TRUE );
listEntry = DfspGetOfflineEntry(&dfsRootName);
if (listEntry == NULL) {
InsertTailList( &DfsData.OfflineRoots, &server->ListEntry);
}
ExReleaseResourceLite( &DfsData.Resource );
if (listEntry != NULL) {
ExFreePool(NewName.Buffer);
ExFreePool(server);
}
DfsDbgTrace(-1, Dbg, "DfspMarkServerOffline exit STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS;
}
//+-------------------------------------------------------------------------
//
// Function: DfspIsRootOnline -- Checks if a server is online
//
// Synopsis: DfspIsRootOnline checks if the server is marked in its offline
// database. If not, the server is online.
//
// Arguments: [servername] - Name of the server of interest.
//
// Returns: NTSTATUS. (Success or STATUS_DEVICE_OFFLINE)
//
//--------------------------------------------------------------------------
NTSTATUS
DfspIsRootOnline(
PUNICODE_STRING FileName,
BOOLEAN CSCAgentCreate)
{
NTSTATUS status;
PLIST_ENTRY listEntry;
UNICODE_STRING dfsRootName;
ULONG i;
DfsDbgTrace(+1, Dbg, "DfspIsRootOnline: %wZ\n", FileName);
if (CSCAgentCreate == TRUE) {
DfsDbgTrace(-1, Dbg, "CSCAgent, returning success!\n", 0);
status = STATUS_SUCCESS;
}
else {
dfsRootName = *FileName;
if (dfsRootName.Buffer[0] == UNICODE_PATH_SEP) {
for (i = 1;
i < dfsRootName.Length/sizeof(WCHAR) &&
dfsRootName.Buffer[i] != UNICODE_PATH_SEP;
i++) {
NOTHING;
}
dfsRootName.Length = (USHORT) ((i-1) * sizeof(WCHAR));
dfsRootName.MaximumLength = dfsRootName.Length;
dfsRootName.Buffer = &FileName->Buffer[1];
}
ExAcquireResourceSharedLite( &DfsData.Resource, TRUE );
listEntry = DfspGetOfflineEntry(&dfsRootName);
ExReleaseResourceLite( &DfsData.Resource );
if (listEntry != NULL) {
status = STATUS_DEVICE_OFF_LINE;
}
else {
status = STATUS_SUCCESS;
}
}
DfsDbgTrace(-1, Dbg, "DfspIsRootOnline: ret 0x%x\n", ULongToPtr(status) );
return status;
}
#if 0
NTSTATUS
DnrGetTargetInfo(
PDNR_CONTEXT pDnrContext)
{
UNICODE_STRING dfsRoot, dfsShare;
PUNICODE_STRING pFileName = &pDnrContext->FileName;
USHORT i, j;
NTSTATUS Status;
dfsRoot.Length = dfsRoot.MaximumLength = 0;
dfsRoot.Buffer = &pFileName->Buffer[1];
for (i = 1;
i < pFileName->Length/sizeof(WCHAR) &&
pFileName->Buffer[i] != UNICODE_PATH_SEP;
i++) {
NOTHING;
}
for (j = i + 1;
j < pFileName->Length/sizeof(WCHAR) &&
pFileName->Buffer[j] != UNICODE_PATH_SEP;
j++) {
NOTHING;
}
if ((pFileName->Buffer[i] == UNICODE_PATH_SEP) && (j > i)) {
dfsRoot.MaximumLength = dfsRoot.Length = (i - 1) * sizeof(WCHAR);
dfsShare.MaximumLength = dfsShare.Length = (j - i - 1) * sizeof(WCHAR);
dfsShare.Buffer = &pFileName->Buffer[i+1];
Status = PktGetTargetInfo( pDnrContext->DCConnFile,
&dfsRoot,
&dfsShare,
&pDnrContext->pNewTargetInfo );
}
else {
Status = STATUS_BAD_NETWORK_PATH;
}
return Status;
}
#endif
NTSTATUS
DnrGetTargetInfo(
PDNR_CONTEXT pDnrContext)
{
NTSTATUS Status = STATUS_SUCCESS;
PDFS_PKT_ENTRY pEntry;
pEntry = pDnrContext->pPktEntry;
if (pEntry != NULL)
{
pDnrContext->pNewTargetInfo = pEntry->pDfsTargetInfo;
if (pDnrContext->pNewTargetInfo != NULL)
{
PktAcquireTargetInfo(pDnrContext->pNewTargetInfo);
}
}
if (pDnrContext->pNewTargetInfo == NULL)
{
if (pDnrContext->pDfsTargetInfo != NULL)
{
pDnrContext->pNewTargetInfo = pDnrContext->pDfsTargetInfo;
if (pDnrContext->pNewTargetInfo != NULL)
{
PktAcquireTargetInfo(pDnrContext->pNewTargetInfo);
}
}
}
return Status;
}