mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5167 lines
163 KiB
5167 lines
163 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
}
|
|
|