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.
2045 lines
66 KiB
2045 lines
66 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
transitn.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines for transitioning from the connected mode
|
|
and vice versa
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman [SethuR] 11 - November - 1997
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
The transition of a connection from a connected mode to a disconnected mode
|
|
and vice versa is guided by the principles of transparency and fideltiy.
|
|
|
|
The principle of transparency demands that the transition be made smoothly
|
|
on the detection of the appropriate condition without any user intervention
|
|
if at all possible and the principle of fidelity relies upon the notion of
|
|
truth and the responsibility for its maintenance. If we wish to adhere to the
|
|
opinion that the client has the truth at all times and the server is merely
|
|
a convenient repositiory for snapshots of the truth one set of semantics falls
|
|
out. On the other hand we could insist that the server has the truth at all
|
|
times and the client caches snapshots of the truth for offline availability
|
|
and performance gains from avoiding network traffic a different set of
|
|
semantics falls out. Note that under certain scenarios the both schemes yield
|
|
identical results, i.e., absence of file sharing.
|
|
|
|
Transitioning from connected mode to disconnected mode
|
|
------------------------------------------------------
|
|
|
|
When transitioning from connected mode it is important to consider existing
|
|
connections and the existing file system objects. In the mini redirector
|
|
terminology it is the SRV_CALL, NET_ROOT instances and the FCB instances that
|
|
are important.
|
|
|
|
The trigger for the transition is normally due to the occurence of one of the
|
|
following two events.
|
|
|
|
1) all the existing transports are going away, because the user has unplugged
|
|
the net.
|
|
|
|
2) an ongoing operation on the connection returns an error that indicates that
|
|
the server is no longer accessible.
|
|
|
|
These two cases are different -- the first one indicates the unavailability
|
|
of net for a potentially long period of time and the second one indicates a
|
|
transient loss of the net. Consequently we treat these two different events
|
|
in different ways -- the first one triggers a top down transition to a
|
|
disconnected mode while the second one triggers a bottom up transition to a
|
|
disconnected mode. As an example consider the case when we have two files
|
|
foo.doc, foo1.doc open on a particular share. When we get an indication that
|
|
the net is no longer available, we mark the SRV_CALL and NET_ROOT instances
|
|
as having transitioned to the disconnected mode. This automatically entails
|
|
that as file system operations are performed on the various open files, foo.doc
|
|
foo1.doc respectively the corresponding transition occurs.
|
|
|
|
On the other hand if there was an error in a particular operation of foo.doc
|
|
then the transition to disconected mode is done for the appropriate FCB alone.
|
|
Thus if we open a new file immediately after that and the net becomes
|
|
available we go on the net for opening the second file.
|
|
|
|
However, owing to the multi step renames that apps use we forego this option.
|
|
Thus the following distinction needs to be made. When no FCB instances are
|
|
open and an error occurs we delay the transition till a open request comes
|
|
through. This will allow us to mask some transient failures on the NET.
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "acd.h"
|
|
#include "acdapi.h"
|
|
#include "ntddmup.h"
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
#define Dbg (DEBUG_TRACE_MRXSMBCSC_TRANSITN)
|
|
RXDT_DefineCategory(MRXSMBCSC_TRANSITN);
|
|
|
|
#define CSC_AUTODIAL_POLL_COUNT 10
|
|
#define INVALID_SESSION_ID 0xffffffff
|
|
|
|
BOOLEAN
|
|
CscIsSpecialShare(
|
|
PUNICODE_STRING ShareName);
|
|
|
|
#define UNICODE_STRING_STRUCT(s) \
|
|
{sizeof(s) - sizeof(WCHAR), sizeof(s) - sizeof(WCHAR), (s)}
|
|
|
|
static UNICODE_STRING CscSpecialShares[] = {
|
|
UNICODE_STRING_STRUCT(L"PIPE"),
|
|
UNICODE_STRING_STRUCT(L"IPC$"),
|
|
UNICODE_STRING_STRUCT(L"ADMIN$"),
|
|
UNICODE_STRING_STRUCT(L"MAILSLOT")
|
|
};
|
|
|
|
KEVENT CscServerEntryTransitioningEvent;
|
|
FAST_MUTEX CscServerEntryTransitioningMutex;
|
|
|
|
PSMBCEDB_SERVER_ENTRY CscServerEntryBeingTransitioned = NULL;
|
|
ULONG CscSessionIdCausingTransition = 0;
|
|
HSHARE CscShareHandlePassedToAgent;
|
|
BOOLEAN vfRetryFromUI = FALSE;
|
|
PSMBCEDB_SERVER_ENTRY CscDfsRootServerEntryBeingTransitioned = NULL;
|
|
|
|
BOOLEAN CscDisableOfflineOperation = FALSE;
|
|
ULONG hTransitionMutexOwner=0;
|
|
|
|
BOOLEAN CSCCheckForAcd(VOID);
|
|
|
|
BOOLEAN CscTransitnOKToGoOffline(
|
|
NTSTATUS RemoteStatus
|
|
);
|
|
|
|
BOOLEAN
|
|
CscIsServerOffline(
|
|
PWCHAR ServerName)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initiates the processing of a transition request by notifying
|
|
the agent and waiting for the response.
|
|
|
|
Arguments:
|
|
|
|
ServerName - the server name
|
|
|
|
|
|
Return Value:
|
|
|
|
returns TRUE if the server entry is offline
|
|
|
|
Notes:
|
|
|
|
If ServerName is NULL we return the status of the Net
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN ServerOffline;
|
|
DWORD cntSlashes;
|
|
UNICODE_STRING uniTemp;
|
|
|
|
ServerOffline = (CscNetPresent == 0);
|
|
|
|
if (ServerName != NULL) {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
USHORT ServerNameLengthInBytes;
|
|
PWCHAR pTempName;
|
|
UNICODE_STRING ServerNameString;
|
|
|
|
ServerOffline = FALSE;
|
|
|
|
pTempName = ServerName;
|
|
ServerNameLengthInBytes = 0;
|
|
cntSlashes = 0;
|
|
|
|
if (*pTempName == L'\\')
|
|
{
|
|
++pTempName;
|
|
++cntSlashes;
|
|
}
|
|
|
|
if (*pTempName == L'\\')
|
|
{
|
|
++pTempName;
|
|
++cntSlashes;
|
|
}
|
|
|
|
// we allow \\servername or servername (with no \\)
|
|
if (cntSlashes == 1)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
while (*pTempName++ != L'\0') {
|
|
ServerNameLengthInBytes += sizeof(WCHAR);
|
|
}
|
|
|
|
ServerNameString.MaximumLength = ServerNameString.Length = ServerNameLengthInBytes;
|
|
ServerNameString.Buffer = ServerName+cntSlashes;
|
|
|
|
SmbCeAcquireResource();
|
|
|
|
try
|
|
{
|
|
pServerEntry = SmbCeGetFirstServerEntry();
|
|
while (pServerEntry != NULL) {
|
|
|
|
uniTemp = pServerEntry->Name;
|
|
|
|
// skip the single backslash on the server entry name
|
|
uniTemp.Length -= sizeof(WCHAR);
|
|
uniTemp.Buffer += 1;
|
|
|
|
if (uniTemp.Length == ServerNameLengthInBytes) {
|
|
if (RtlCompareUnicodeString(
|
|
&uniTemp,
|
|
&ServerNameString,
|
|
TRUE) == 0) {
|
|
|
|
ServerOffline = SmbCeIsServerInDisconnectedMode(pServerEntry);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
SmbCeReleaseResource();
|
|
return FALSE;
|
|
}
|
|
|
|
SmbCeReleaseResource();
|
|
|
|
if (!pServerEntry && !CscNetPresent)
|
|
{
|
|
HSHARE CscShareHandle = 0;
|
|
ULONG ulRootHintFlags=0;
|
|
|
|
GetHShareFromUNCString(
|
|
ServerNameString.Buffer,
|
|
ServerNameString.Length,
|
|
2, // No double-leading backslashes in the name passed in
|
|
FALSE, // server name
|
|
&CscShareHandle,
|
|
&ulRootHintFlags);
|
|
ServerOffline = (CscShareHandle != 0);
|
|
}
|
|
}
|
|
|
|
return ServerOffline;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CscTakeServerOffline(
|
|
PWCHAR ServerName)
|
|
{
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
UNICODE_STRING ServerNameString;
|
|
UNICODE_STRING tmpSrvName;
|
|
NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
// DbgPrint("CscTakeServerOffline(%ws)\n", ServerName);
|
|
|
|
if (ServerName == NULL) {
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
goto AllDone;
|
|
}
|
|
// Clip leading backslashes
|
|
while (*ServerName == L'\\') {
|
|
ServerName++;
|
|
}
|
|
RtlInitUnicodeString(&ServerNameString, ServerName);
|
|
// Scan list of server entries looking for this one
|
|
SmbCeAcquireResource();
|
|
try {
|
|
pServerEntry = SmbCeGetFirstServerEntry();
|
|
while (pServerEntry != NULL) {
|
|
if (pServerEntry->Server.CscState == ServerCscShadowing) {
|
|
if (pServerEntry->DfsRootName.Length > 0) {
|
|
tmpSrvName = pServerEntry->DfsRootName;
|
|
tmpSrvName.Length -= sizeof(WCHAR);
|
|
tmpSrvName.Buffer += 1;
|
|
if (RtlCompareUnicodeString(&tmpSrvName, &ServerNameString, TRUE) == 0)
|
|
break;
|
|
} else {
|
|
tmpSrvName = pServerEntry->Name;
|
|
tmpSrvName.Length -= sizeof(WCHAR);
|
|
tmpSrvName.Buffer += 1;
|
|
if (RtlCompareUnicodeString(&tmpSrvName, &ServerNameString, TRUE) == 0)
|
|
break;
|
|
}
|
|
}
|
|
pServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SmbCeReleaseResource();
|
|
Status = ERROR_INVALID_PARAMETER;
|
|
}
|
|
if (pServerEntry != NULL) {
|
|
// DbgPrint("Found ServerEntry@0x%x\n", pServerEntry);
|
|
SmbCeReferenceServerEntry(pServerEntry);
|
|
SmbCeReleaseResource();
|
|
Status = CscTransitionServerEntryForDisconnectedOperation(
|
|
pServerEntry,
|
|
NULL,
|
|
STATUS_BAD_NETWORK_NAME,
|
|
FALSE);
|
|
// Mark it so it will not auto-reconnect
|
|
if (Status == STATUS_SUCCESS)
|
|
pServerEntry->Server.IsPinnedOffline = TRUE;
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
} else {
|
|
SmbCeReleaseResource();
|
|
}
|
|
|
|
AllDone:
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
CscCheckWithAgentForTransitioningServerEntry(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
ULONG SessionId,
|
|
HSHARE AgentShareHandle,
|
|
BOOLEAN fInvokeAutoDial,
|
|
BOOLEAN *lpfRetryFromUI,
|
|
PSMBCEDB_SERVER_ENTRY *pDfsRootServerEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initiates the processing of a transition request by notifying
|
|
the agent and waiting for the response.
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry
|
|
|
|
pNetRootEntry - the net root entry instance
|
|
|
|
Return Value:
|
|
|
|
returns TRUE if the server entry was transitioned for offlien operation
|
|
|
|
--*/
|
|
{
|
|
LONG cntTransportsForCSC=0;
|
|
BOOLEAN TransitionedServerEntry, OkToTransition = FALSE;
|
|
PSMBCEDB_SERVER_ENTRY pTempServerEntry = NULL;
|
|
|
|
if(!MRxSmbIsCscEnabled) {
|
|
return(FALSE);
|
|
}
|
|
|
|
// DbgPrint("CscCheckWithAgent %wZ \n", &pServerEntry->Name);
|
|
|
|
ExAcquireFastMutex(&CscServerEntryTransitioningMutex);
|
|
|
|
hTransitionMutexOwner = GetCurThreadHandle();
|
|
if (pServerEntry->DfsRootName.Length != 0)
|
|
{
|
|
PSMBCEDB_SERVER_ENTRY pThisServerEntry;
|
|
PSMBCEDB_SERVER_ENTRY pNextServerEntry;
|
|
|
|
SmbCeAcquireResource();
|
|
|
|
pThisServerEntry = SmbCeGetFirstServerEntry();
|
|
|
|
while (pThisServerEntry != NULL) {
|
|
pNextServerEntry = SmbCeGetNextServerEntry(pThisServerEntry);
|
|
|
|
if (RtlEqualUnicodeString(&pServerEntry->DfsRootName,
|
|
&pThisServerEntry->Name,
|
|
TRUE)) {
|
|
|
|
// DbgPrint("CscCheckWithAgent DfsRoot %wZ \n", &pThisServerEntry->Name);
|
|
pTempServerEntry = pThisServerEntry;
|
|
|
|
break;
|
|
}
|
|
|
|
pThisServerEntry = pNextServerEntry;
|
|
}
|
|
SmbCeReleaseResource();
|
|
}
|
|
|
|
|
|
CscServerEntryBeingTransitioned = pServerEntry;
|
|
CscDfsRootServerEntryBeingTransitioned = pTempServerEntry;
|
|
CscShareHandlePassedToAgent = AgentShareHandle;
|
|
vfRetryFromUI = FALSE;
|
|
|
|
KeResetEvent(&CscServerEntryTransitioningEvent);
|
|
|
|
OkToTransition = (!SmbCeIsServerInDisconnectedMode(pServerEntry)||
|
|
(pTempServerEntry && !SmbCeIsServerInDisconnectedMode(pTempServerEntry)));
|
|
|
|
if (OkToTransition) {
|
|
|
|
// This is dropped in MRxSmbCscSignalAgent
|
|
EnterShadowCrit();
|
|
|
|
SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_SHARE_DISCONNECTED);
|
|
if (fInvokeAutoDial)
|
|
{
|
|
SetFlag(sGS.uFlagsEvents,FLAG_GLOBALSTATUS_INVOKE_AUTODIAL);
|
|
}
|
|
sGS.hShareDisconnected = AgentShareHandle;
|
|
|
|
CscSessionIdCausingTransition = SessionId;
|
|
|
|
MRxSmbCscSignalAgent(
|
|
NULL,
|
|
SIGNALAGENTFLAG_CONTINUE_FOR_NO_AGENT);
|
|
|
|
KeWaitForSingleObject(
|
|
&CscServerEntryTransitioningEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
}
|
|
|
|
TransitionedServerEntry = (SmbCeIsServerInDisconnectedMode(pServerEntry) &&
|
|
(!pTempServerEntry || SmbCeIsServerInDisconnectedMode(pTempServerEntry)));
|
|
|
|
*pDfsRootServerEntry = pTempServerEntry;
|
|
|
|
CscServerEntryBeingTransitioned = NULL;
|
|
CscShareHandlePassedToAgent = 0;
|
|
CscDfsRootServerEntryBeingTransitioned = NULL;
|
|
CscSessionIdCausingTransition = 0;
|
|
*lpfRetryFromUI = vfRetryFromUI;
|
|
hTransitionMutexOwner = 0;
|
|
ExReleaseFastMutex(&CscServerEntryTransitioningMutex);
|
|
|
|
return TransitionedServerEntry;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscTransitionServerToOffline(
|
|
ULONG SessionId,
|
|
HSHARE hShare,
|
|
ULONG TransitionStatus)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the RDR data structures based upon the decision of the
|
|
agent
|
|
|
|
Arguments:
|
|
|
|
hShare - the shadow handle to the server
|
|
|
|
TransitionStatus -- it is tri state value.
|
|
0 implies retry the operation.
|
|
1 transition this server for offline operation
|
|
anything else means fail
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
LONG CscState = ServerCscShadowing;
|
|
|
|
if(!MRxSmbIsCscEnabled) {
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
// DbgPrint("CscTransitionServerToOffline: Share 0x%x SessionId 0x%x (vs 0x%x)\n",
|
|
// hShare,
|
|
// SessionId,
|
|
// CscSessionIdCausingTransition);
|
|
|
|
switch (TransitionStatus) {
|
|
case 1 :
|
|
if (fShadow && // only if CSC is turned ON by the agent do we go disconnected
|
|
CscServerEntryBeingTransitioned && // there is a server entry (this must be true
|
|
CscSessionIdCausingTransition == SessionId && // The right session
|
|
CscShareHandlePassedToAgent // and we have a share in the database
|
|
)
|
|
{
|
|
// then it is OK to go disconnected
|
|
CscState = ServerCscDisconnected;
|
|
}
|
|
break;
|
|
|
|
|
|
case 0 : // UI said retry
|
|
vfRetryFromUI = TRUE;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
|
|
if (CscServerEntryBeingTransitioned != NULL && SessionId == CscSessionIdCausingTransition) {
|
|
// DbgPrint("CscTransitionServerToOffline %wZ \n", &CscServerEntryBeingTransitioned->Name);
|
|
InterlockedExchange(
|
|
&CscServerEntryBeingTransitioned->Server.CscState,
|
|
CscState);
|
|
|
|
// DbgPrint("CscTransitionServerToOffline %wZ Sess 0x%x\n",
|
|
// &CscServerEntryBeingTransitioned->Name,
|
|
// SessionId);
|
|
|
|
if (CscDfsRootServerEntryBeingTransitioned)
|
|
{
|
|
// if this is an alternate, then also put the
|
|
// dfs root in disconnected state if it isn't already
|
|
|
|
if (!SmbCeIsServerInDisconnectedMode(CscDfsRootServerEntryBeingTransitioned))
|
|
{
|
|
SmbCeReferenceServerEntry(CscDfsRootServerEntryBeingTransitioned);
|
|
}
|
|
|
|
InterlockedExchange(
|
|
&CscDfsRootServerEntryBeingTransitioned->Server.CscState,
|
|
CscState);
|
|
|
|
}
|
|
|
|
// Signal the event on which the other requests in the RDR are waiting
|
|
KeSetEvent(
|
|
&CscServerEntryTransitioningEvent,
|
|
0,
|
|
FALSE );
|
|
} else {
|
|
// ASSERT(!"No server entry is transitioning to offline");
|
|
}
|
|
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
CscPrepareServerEntryForOnlineOperation(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
BOOL fGoAllTheWay
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transitions a given server entry for online operation
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry that needs to be transitioned
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
PSMBCEDB_SESSION_ENTRY pSessionEntry;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
|
|
LONG CscState;
|
|
|
|
SmbCeLog(("Transition SE %lx fGoAllTheWay=%d\n",pServerEntry, fGoAllTheWay));
|
|
SmbLog(LOG,
|
|
CscPrepareServerEntryForOnlineOperation_1,
|
|
LOGULONG(fGoAllTheWay)
|
|
LOGPTR(pServerEntry)
|
|
LOGUSTR(pServerEntry->Name));
|
|
|
|
if (fGoAllTheWay)
|
|
{
|
|
CscState = InterlockedCompareExchange(
|
|
&pServerEntry->Server.CscState,
|
|
ServerCscTransitioningToShadowing,
|
|
ServerCscDisconnected);
|
|
if(pServerEntry->Server.IsFakeDfsServerForOfflineUse == TRUE)
|
|
{
|
|
HookKdPrint(TRANSITION, ("CscPrepareServerEntryForOnlineOperation: %x is a FAKE DFS entry, mark it for destruction \n", pServerEntry));
|
|
pServerEntry->Header.State = SMBCEDB_DESTRUCTION_IN_PROGRESS;
|
|
}
|
|
|
|
SmbCeLog(("Transition SE %lx %wZ fGoAllTheWay CscState=%x\n",pServerEntry, &pServerEntry->Name, CscState));
|
|
SmbLog(LOG,
|
|
CscPrepareServerEntryForOnlineOperation_2,
|
|
LOGULONG(CscState)
|
|
LOGPTR(pServerEntry)
|
|
LOGUSTR(pServerEntry->Name));
|
|
}
|
|
|
|
if (!fGoAllTheWay || (CscState == ServerCscDisconnected)) {
|
|
SmbCeLog(("Transition SE CO %lx, fGoAllTheWay=%d\n",pServerEntry, fGoAllTheWay));
|
|
SmbLog(LOG,
|
|
CscPrepareServerEntryForOnlineOperation_3,
|
|
LOGULONG(fGoAllTheWay)
|
|
LOGPTR(pServerEntry)
|
|
LOGUSTR(pServerEntry->Name));
|
|
|
|
InterlockedCompareExchange(
|
|
&pServerEntry->Header.State,
|
|
SMBCEDB_DESTRUCTION_IN_PROGRESS,
|
|
SMBCEDB_ACTIVE);
|
|
|
|
SmbCeReferenceServerEntry(pServerEntry);
|
|
SmbCeResumeAllOutstandingRequestsOnError(pServerEntry);
|
|
pServerEntry->ServerStatus = STATUS_CONNECTION_DISCONNECTED;
|
|
|
|
if (fGoAllTheWay)
|
|
{
|
|
MRxSmbCSCResumeAllOutstandingOperations(pServerEntry);
|
|
pServerEntry->Server.CscState = ServerCscShadowing;
|
|
pServerEntry->Server.IsPinnedOffline = FALSE;
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
CscPrepareServerEntryForOnlineOperationFull(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transitions a given server entry for online operation
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry that needs to be transitioned
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
CscPrepareServerEntryForOnlineOperation(pServerEntry, TRUE);
|
|
}
|
|
|
|
VOID
|
|
CscPrepareServerEntryForOnlineOperationPartial(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transitions a given server entry for online operation
|
|
|
|
Arguments:
|
|
|
|
pServerEntry - the server entry that needs to be transitioned
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
CscPrepareServerEntryForOnlineOperation(pServerEntry, FALSE);
|
|
}
|
|
|
|
NTSTATUS
|
|
CscTransitionServerToOnline(
|
|
HSHARE hShare)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates the RDR data structures based upon the decision of the
|
|
agent
|
|
|
|
Arguments:
|
|
|
|
hShare - the shadow handle to the server
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
SHAREINFOW sSR;
|
|
NTSTATUS Status=STATUS_INVALID_PARAMETER;
|
|
|
|
if (hShare == 0) {
|
|
|
|
Status=STATUS_SUCCESS;
|
|
|
|
SmbCeLog(("Transtioning all servers online \n"));
|
|
SmbLog(LOG,
|
|
CscTransitionServerToOnline_1,
|
|
LOGULONG(hShare));
|
|
SmbCeAcquireResource();
|
|
pServerEntry = SmbCeGetFirstServerEntry();
|
|
while (pServerEntry != NULL) {
|
|
PSMBCEDB_SERVER_ENTRY pNextServerEntry;
|
|
|
|
pNextServerEntry = SmbCeGetNextServerEntry(pServerEntry);
|
|
CscPrepareServerEntryForOnlineOperationFull(pServerEntry);
|
|
pServerEntry = pNextServerEntry;
|
|
}
|
|
SmbCeReleaseResource();
|
|
|
|
} else {
|
|
int iRet;
|
|
|
|
EnterShadowCrit();
|
|
iRet = GetShareInfo(hShare, &sSR, NULL);
|
|
LeaveShadowCrit();
|
|
SmbCeLog(("Transtioning %ls online \n", sSR.rgSharePath));
|
|
SmbLog(LOG,
|
|
CscTransitionServerToOnline_2,
|
|
LOGWSTR(sSR.rgSharePath));
|
|
|
|
if (iRet >= 0)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
if ((FindServerEntryFromCompleteUNCPath(sSR.rgSharePath, &pServerEntry)) == STATUS_SUCCESS)
|
|
{
|
|
PSMBCEDB_SERVER_ENTRY pThisServerEntry;
|
|
PSMBCEDB_SERVER_ENTRY pNextServerEntry;
|
|
|
|
// DbgPrint("Close all open files on %wZ\n", &pServerEntry->Name);
|
|
CloseOpenFiles(hShare, &pServerEntry->Name, 1); // skip one slash
|
|
SmbCeAcquireResource();
|
|
|
|
pThisServerEntry = SmbCeGetFirstServerEntry();
|
|
while (pThisServerEntry != NULL) {
|
|
pNextServerEntry = SmbCeGetNextServerEntry(pThisServerEntry);
|
|
|
|
if (pThisServerEntry != pServerEntry &&
|
|
pThisServerEntry->DfsRootName.Length != 0) {
|
|
if (RtlEqualUnicodeString(&pThisServerEntry->DfsRootName,
|
|
&pServerEntry->Name,
|
|
TRUE)) {
|
|
SmbCeLog(("Go online ServerEntry With DFS name %x\n",pThisServerEntry));
|
|
SmbLog(LOG,
|
|
CscTransitionServerToOnline_3,
|
|
LOGPTR(pThisServerEntry)
|
|
LOGUSTR(pThisServerEntry->Name));
|
|
|
|
CscPrepareServerEntryForOnlineOperationFull(pThisServerEntry);
|
|
}
|
|
}
|
|
|
|
pThisServerEntry = pNextServerEntry;
|
|
}
|
|
|
|
CscPrepareServerEntryForOnlineOperationFull(pServerEntry);
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
|
|
SmbCeReleaseResource();
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscpTransitionServerEntryForDisconnectedOperation(
|
|
RX_CONTEXT *RxContext,
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry,
|
|
NTSTATUS RemoteStatus,
|
|
BOOLEAN fInvokeAutoDial,
|
|
ULONG uFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transitions the server entry for disconnected mode of
|
|
operation
|
|
|
|
Arguments:
|
|
|
|
pServerEntry -- the server entry instance to be transitioned
|
|
|
|
pNetRootEntry -- the net root entry instance
|
|
|
|
RemoteStatus -- the failed status of the remote operation
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
If this routine returns STATUS_RETRY it implies that the associated server
|
|
entry has been successfully trnaisitioned for disconnected operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN TransitionServerEntryToDisconnectedMode, fRetryFromUI=FALSE;
|
|
ULONG ulRootHintFlags=0;
|
|
LONG CscState, cntTransports=0;
|
|
ULONG SessionId = INVALID_SESSION_ID;
|
|
|
|
SmbCeLog(("CscTrPSrv IN DFSFlgs %x\n",uFlags));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_1,
|
|
LOGULONG(uFlags));
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
!CscTransitnOKToGoOffline(RemoteStatus) ||
|
|
!(uFlags & DFS_FLAG_LAST_ALTERNATE) ||
|
|
pServerEntry->Server.IsLoopBack) {
|
|
|
|
SmbCeLog(("CscTrPSrv Out RemoteStatus=%x\n",RemoteStatus));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_2,
|
|
LOGULONG(RemoteStatus));
|
|
return(RemoteStatus);
|
|
}
|
|
|
|
// if we are supposed to invoke autodial, check if autodial service is running
|
|
// this will ensure that we don't go up in usermode when we shouldn't.
|
|
|
|
if (fInvokeAutoDial) {
|
|
fInvokeAutoDial = CSCCheckForAcd();
|
|
}
|
|
|
|
SmbCeLog(("CscTrPSrv Autodial %x\n",fInvokeAutoDial));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_3,
|
|
LOGUCHAR(fInvokeAutoDial));
|
|
|
|
if (!fInvokeAutoDial) {
|
|
// Notify the CSC agent of any transport changes if required
|
|
CscNotifyAgentOfNetStatusChangeIfRequired(FALSE);
|
|
}
|
|
|
|
// Ensure that we are never called to prepare for a transition if the remote
|
|
// operation was successful.
|
|
ASSERT(RemoteStatus != STATUS_SUCCESS);
|
|
|
|
// The transition to disconnected operation is a three step process...
|
|
// If the remote status is not one of the list of statuses that can signal
|
|
// a transition to disconnected operation relect the remote status back.
|
|
|
|
Status = RemoteStatus;
|
|
|
|
if (CscDisableOfflineOperation) {
|
|
return Status;
|
|
}
|
|
|
|
CscState = InterlockedCompareExchange(
|
|
&pServerEntry->Server.CscState,
|
|
ServerCscTransitioningToDisconnected,
|
|
ServerCscShadowing);
|
|
|
|
if (CscState == ServerCscShadowing) {
|
|
HSHARE CscShareHandle = 0;
|
|
|
|
if (pNetRootEntry != NULL) {
|
|
CscShareHandle = pNetRootEntry->NetRoot.sCscRootInfo.hShare;
|
|
}
|
|
|
|
/***********************************************************************************************
|
|
|
|
ACHTUNG !!! do not hold the shadow critical section here
|
|
This may cause a deadlock, as a paging read could come down this way because
|
|
a server has gone down. The guy doing the paging read may be holding the
|
|
VCB and the FCB locks on FAT. Some other thread might own the shadowcritsect
|
|
and may be trying to open a file. This would cause it to try to acquire the
|
|
VCB and hence block. Thus we have classic deadlock situation.
|
|
|
|
This happens only on FAT.
|
|
|
|
The only consequence of not holding the critsect here is that we
|
|
may get a false warning for a share that used to be in the database but has gotten
|
|
deleted. It would be very difficult to hit that timing window so the
|
|
chances of this happening are slim to none.
|
|
|
|
There is another aspect to this solution, which is to make sure that handle for
|
|
the shares inode is always kept open, so FAT will never have to hold the VCB
|
|
|
|
|
|
***********************************************************************************************/
|
|
|
|
// do any CSC operations only if it is indeed enabled
|
|
if (fShadow )
|
|
{
|
|
if (CscShareHandle == 0) {
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
UNICODE_STRING uUncName = {0, 0, NULL};
|
|
UNICODE_STRING uShareName = {0, 0, NULL};
|
|
BOOL fIsShareName = FALSE;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
PQUERY_PATH_REQUEST QpReq;
|
|
ULONG cntSlashes = 0;
|
|
ULONG i;
|
|
|
|
if (RxContext != NULL && RxContext->CurrentIrpSp != NULL) {
|
|
IrpSp = RxContext->CurrentIrpSp;
|
|
//
|
|
// If this is a create AND a dfs path, use the dfs path passed in
|
|
//
|
|
if (IrpSp->MajorFunction == IRP_MJ_CREATE) {
|
|
pDfsNameContext = CscIsValidDfsNameContext(
|
|
RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
if (pDfsNameContext != NULL) {
|
|
// DbgPrint("DfsNameContext UNCFileName=[%wZ]\n",
|
|
// &pDfsNameContext->UNCFileName);
|
|
uUncName = pDfsNameContext->UNCFileName;
|
|
fIsShareName = TRUE;
|
|
}
|
|
//
|
|
// If this is a query ioctl, use the path we're querying
|
|
//
|
|
} else if (IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL
|
|
&&
|
|
IrpSp->MinorFunction == 0
|
|
&&
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_REDIR_QUERY_PATH
|
|
) {
|
|
QpReq =
|
|
(PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
uUncName.Buffer = QpReq->FilePathName;
|
|
uUncName.Length = (USHORT) QpReq->PathNameLength;
|
|
uUncName.MaximumLength = uUncName.Length;
|
|
fIsShareName = TRUE;
|
|
}
|
|
}
|
|
//
|
|
// Not a dfs create nor a query path - use the redir's netrootentry,
|
|
// if we have one
|
|
//
|
|
if (uUncName.Buffer == NULL && pNetRootEntry && pNetRootEntry->Name.Length) {
|
|
uUncName = pNetRootEntry->Name;
|
|
fIsShareName = TRUE;
|
|
}
|
|
//
|
|
// Bottom out using the server entry, either the dfsrootname
|
|
// or the serverentry name. This will take the server offline
|
|
// w/o regard to which share the error was in reference to.
|
|
//
|
|
if (uUncName.Buffer == NULL) {
|
|
if (pServerEntry->DfsRootName.Buffer) {
|
|
uUncName = pServerEntry->DfsRootName;
|
|
} else {
|
|
uUncName = pServerEntry->Name;
|
|
}
|
|
}
|
|
//
|
|
// Be sure all we have is \server\share, or \server,
|
|
//
|
|
for (cntSlashes = i = 0; i < uUncName.Length/sizeof(WCHAR); i++) {
|
|
if (uUncName.Buffer[i] == L'\\')
|
|
cntSlashes++;
|
|
if (cntSlashes >= 3) {
|
|
uUncName.Length = (USHORT) (sizeof(WCHAR) * i);
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// If this is a special share (like IPC$), treat it as a valid
|
|
// share to go offline against. (IE, we go offline against
|
|
// \server\IPC$)
|
|
if (fIsShareName == TRUE) {
|
|
uShareName = uUncName;
|
|
for (cntSlashes = i = 0; i < uUncName.Length/sizeof(WCHAR); i++) {
|
|
uShareName.Buffer++;
|
|
uShareName.Length -= sizeof(WCHAR);
|
|
if (uUncName.Buffer[i] == L'\\')
|
|
cntSlashes++;
|
|
if (cntSlashes == 2)
|
|
break;
|
|
}
|
|
if (CscIsSpecialShare(&uShareName) == TRUE) {
|
|
fIsShareName = FALSE;
|
|
// revert to just \servername
|
|
uUncName.Length -= uShareName.Length + sizeof(WCHAR);
|
|
}
|
|
}
|
|
GetHShareFromUNCString(
|
|
uUncName.Buffer,
|
|
uUncName.Length,
|
|
1,
|
|
fIsShareName,
|
|
&CscShareHandle,
|
|
&ulRootHintFlags);
|
|
|
|
ulRootHintFlags &= ~FLAG_CSC_HINT_PIN_SYSTEM;
|
|
|
|
// DbgPrint("CscpTransitionServerEntry: [%wZ] CSCHandle=%x\n",
|
|
// &uUncName,
|
|
// CscShareHandle);
|
|
RxDbgTrace(0, Dbg, ("CscpTransitionServerEntry: [%wZ] CSCHandle=%x\n",
|
|
&uUncName,
|
|
CscShareHandle));
|
|
} else {
|
|
ulRootHintFlags = 0;
|
|
}
|
|
} // if (fShadow)
|
|
else
|
|
{
|
|
CscShareHandle = 0; // if the agent hasn't turned on CSC
|
|
// then don't tell him for CSC shares
|
|
}
|
|
|
|
|
|
if (fInvokeAutoDial || // either autodial
|
|
(CscShareHandle != 0)) { // or CSC
|
|
|
|
if (MRxSmbCscTransitionEnabledByDefault) {
|
|
|
|
RxDbgTrace(0, Dbg, ("CscTransitionServerEntryForDisconnectedOperation: silently going offline on %wZ\r\n", CscShareHandle, ulRootHintFlags));
|
|
|
|
InterlockedExchange(
|
|
&pServerEntry->Server.CscState,
|
|
ServerCscDisconnected);
|
|
|
|
SmbCeReferenceServerEntry(pServerEntry);
|
|
Status = STATUS_SUCCESS;
|
|
RxDbgTrace(0, Dbg, ("Transitioning Server Entry for DC %lx Status %lx\n",pServerEntry,Status));
|
|
SmbCeLog(("Transitioning Server Entry for DC %lx Status %lx\n",pServerEntry,Status));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_4,
|
|
LOGULONG(Status)
|
|
LOGPTR(pServerEntry)
|
|
LOGUSTR(pServerEntry->Name));
|
|
|
|
} else {
|
|
PSMBCEDB_SERVER_ENTRY pDfsRootServerEntry = NULL;
|
|
PIO_STACK_LOCATION IrpSp = NULL;
|
|
|
|
if (RxContext != NULL && RxContext->CurrentIrpSp != NULL)
|
|
IrpSp = RxContext->CurrentIrpSp;
|
|
|
|
// If the remote status was such that a transition to disconnected operation
|
|
// should be triggerred we need to signal the agent to trigger the appropriate
|
|
// mechanism for involving the client in this decision and decide based on the
|
|
// result.
|
|
|
|
cntTransports = vcntTransportsForCSC;
|
|
|
|
SmbCeLog(("CscTrPSrv ChkAgnt %x\n",CscShareHandle));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_5,
|
|
LOGULONG(CscShareHandle));
|
|
RxDbgTrace(0, Dbg, ("CscTransitionServerEntryForDisconnectedOperation: Checking with agent before going offline on CscShareHandle=%x HintFlags=%x\r\n", CscShareHandle, ulRootHintFlags));
|
|
|
|
// if (RxContext != NULL && RxContext->CurrentIrpSp != NULL) {
|
|
// DbgPrint("** Transition: MJ/MN = 0x%x/0x%x\n",
|
|
// RxContext->CurrentIrpSp->MajorFunction,
|
|
// RxContext->CurrentIrpSp->MinorFunction);
|
|
// }
|
|
|
|
if (
|
|
RxContext
|
|
&&
|
|
RxContext->pRelevantSrvOpen
|
|
&&
|
|
RxContext->pRelevantSrvOpen->pVNetRoot
|
|
) {
|
|
SessionId = RxContext->pRelevantSrvOpen->pVNetRoot->SessionId;
|
|
} else {
|
|
// DbgPrint("** pVnetRoot's sessionid not present\n");
|
|
}
|
|
|
|
if (
|
|
SessionId == INVALID_SESSION_ID
|
|
&&
|
|
IrpSp != NULL
|
|
&&
|
|
IrpSp->MajorFunction == IRP_MJ_CREATE
|
|
) {
|
|
PIO_SECURITY_CONTEXT pSecurityContext;
|
|
PACCESS_TOKEN pAccessToken;
|
|
|
|
// DbgPrint("**CREATE\n");
|
|
pSecurityContext = RxContext->Create.NtCreateParameters.SecurityContext;
|
|
pAccessToken = SeQuerySubjectContextToken(
|
|
&pSecurityContext->AccessState->SubjectSecurityContext);
|
|
if (!SeTokenIsRestricted(pAccessToken))
|
|
SeQuerySessionIdToken(pAccessToken, &SessionId);
|
|
}
|
|
|
|
if (
|
|
SessionId == INVALID_SESSION_ID
|
|
&&
|
|
IrpSp != NULL
|
|
&&
|
|
IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL
|
|
&&
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_REDIR_QUERY_PATH
|
|
) {
|
|
PQUERY_PATH_REQUEST QpReq;
|
|
PSECURITY_SUBJECT_CONTEXT pSecurityContext;
|
|
|
|
// DbgPrint("**QUERY_PATH\n");
|
|
QpReq = (PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
|
|
pSecurityContext = &QpReq->SecurityContext->AccessState->SubjectSecurityContext;
|
|
if (pSecurityContext->ClientToken != NULL)
|
|
SeQuerySessionIdToken(pSecurityContext->ClientToken, &SessionId);
|
|
else
|
|
SeQuerySessionIdToken(pSecurityContext->PrimaryToken, &SessionId);
|
|
}
|
|
|
|
if (SessionId == INVALID_SESSION_ID) {
|
|
// DbgPrint("** Not CREATE or QUERY_PATH...\n");
|
|
if (RxContext != NULL && RxContext->CurrentIrp != NULL)
|
|
IoGetRequestorSessionId(RxContext->CurrentIrp, &SessionId);
|
|
}
|
|
|
|
if (SessionId == INVALID_SESSION_ID) {
|
|
// DbgPrint("All sessionid attempts failed, setting to 0..\n");
|
|
SessionId = 0;
|
|
}
|
|
|
|
// DbgPrint("** CscTrPSrv ChkAgnt SessionId: 0x%x\n", SessionId);
|
|
|
|
if (CscCheckWithAgentForTransitioningServerEntry(
|
|
pServerEntry,
|
|
SessionId,
|
|
CscShareHandle,
|
|
fInvokeAutoDial,
|
|
&fRetryFromUI,
|
|
&pDfsRootServerEntry
|
|
)) {
|
|
|
|
SmbCeReferenceServerEntry(pServerEntry);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
RxDbgTrace(0, Dbg, ("Transitioning Server Entry for DC %lx Status %lx\n",pServerEntry,Status));
|
|
SmbCeLog(("Transitioning Server Entry for DC %lx Status %lx\n",pServerEntry,Status));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_4,
|
|
LOGULONG(Status)
|
|
LOGPTR(pServerEntry)
|
|
LOGUSTR(pServerEntry->Name));
|
|
}
|
|
else if (fRetryFromUI)
|
|
{
|
|
LARGE_INTEGER interval;
|
|
int i;
|
|
|
|
SmbCeLog(("CscTrPSrv UIretry\n"));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_6,
|
|
LOGUCHAR(fRetryFromUI));
|
|
RxDbgTrace(0, Dbg, ("UI sent us rerty, polling for %d seconds\n", CSC_AUTODIAL_POLL_COUNT));
|
|
|
|
for (i=0; i<CSC_AUTODIAL_POLL_COUNT; ++i)
|
|
{
|
|
if(cntTransports != vcntTransportsForCSC)
|
|
{
|
|
Status = STATUS_RETRY;
|
|
RxDbgTrace(0, Dbg, ("A new transport arrived \r\n"));
|
|
break;
|
|
}
|
|
|
|
interval.QuadPart = -1*10*1000*10*100; // 1 second
|
|
|
|
KeDelayExecutionThread( KernelMode, FALSE, &interval );
|
|
}
|
|
|
|
|
|
InterlockedExchange(
|
|
&pServerEntry->Server.CscState,
|
|
ServerCscShadowing);
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
InterlockedExchange(
|
|
&pServerEntry->Server.CscState,
|
|
ServerCscShadowing);
|
|
|
|
}
|
|
} else if (CscState == ServerCscDisconnected) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
SmbCeLog(("CscTrPSrv Out St %x\n",Status));
|
|
SmbLog(LOG,
|
|
CscpTransitionServerEntryForDisconnectedOperation_7,
|
|
LOGULONG(Status));
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation(
|
|
PRX_CONTEXT RxContext)
|
|
{
|
|
BOOLEAN TransitionVNetRoot = FALSE;
|
|
|
|
SmbCeLog(("CSCTrIsDfs IN %x\n", RxContext));
|
|
SmbLog(LOG,
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation_1,
|
|
LOGPTR(RxContext));
|
|
if ((RxContext != NULL) &&
|
|
RxContext->CurrentIrpSp &&
|
|
(RxContext->CurrentIrpSp->MajorFunction == IRP_MJ_CREATE)){
|
|
|
|
NTSTATUS Status;
|
|
PDFS_NAME_CONTEXT pDfsNameContext;
|
|
UNICODE_STRING ShareName;
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(
|
|
RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
|
|
if (pDfsNameContext != NULL) {
|
|
// Ensure that the server handles in the NET_ROOT instance
|
|
// are initialized. This is because the DFS server munges
|
|
// the names and the original DFS name needs to be presented
|
|
// to the user for transitioning.
|
|
|
|
SmbCeLog(("CSCTrIsDfs IsDsf %x\n", pDfsNameContext));
|
|
SmbLog(LOG,
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation_2,
|
|
LOGPTR(pDfsNameContext)
|
|
LOGULONG(pDfsNameContext->NameContextType)
|
|
LOGULONG(pDfsNameContext->Flags)
|
|
LOGUSTR(pDfsNameContext->UNCFileName));
|
|
|
|
Status = CscDfsParseDfsPath(
|
|
&pDfsNameContext->UNCFileName,
|
|
NULL,
|
|
&ShareName,
|
|
NULL);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
SHADOWINFO ShadowInfo;
|
|
int Result;
|
|
|
|
SmbCeLog(("CSCTrDfs Parsed %wZ\n",&ShareName));
|
|
SmbLog(LOG,
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation_3,
|
|
LOGUSTR(ShareName));
|
|
|
|
EnterShadowCrit();
|
|
|
|
TransitionVNetRoot = (FindCreateShareForNt(
|
|
&ShareName,
|
|
FALSE, // do not create a new one
|
|
&ShadowInfo,
|
|
NULL) == SRET_OK);
|
|
|
|
LeaveShadowCrit();
|
|
if (!fShadow && TransitionVNetRoot)
|
|
{
|
|
// DbgPrint("FindCreateServerForNt incorrectly returned TRUE for %wZ\n", &ShareName);
|
|
ASSERT(FALSE);
|
|
}
|
|
if (TransitionVNetRoot)
|
|
{
|
|
SmbCeLog(("CSCTrDfs TrOffl \n"));
|
|
SmbLog(LOG,
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation_4,
|
|
LOGUCHAR(TransitionVNetRoot));
|
|
|
|
// DbgPrint("CSC:transitioning DFS share %wZ to offline hShare=%x shadowinfo=%x\n",&ShareName, ShadowInfo.hShare, &ShadowInfo);
|
|
ASSERT(ShadowInfo.hShare != 0);
|
|
}
|
|
}
|
|
} else {
|
|
TransitionVNetRoot = FALSE;
|
|
}
|
|
} else {
|
|
TransitionVNetRoot = FALSE;
|
|
}
|
|
|
|
SmbCeLog(("CSCTrIsDfs Out %x\n", TransitionVNetRoot));
|
|
SmbLog(LOG,
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation_5,
|
|
LOGUCHAR(TransitionVNetRoot));
|
|
|
|
return TransitionVNetRoot;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscPrepareDfsServerEntryForDisconnectedOperation(
|
|
PSMBCEDB_SERVER_ENTRY pCurrentServerEntry,
|
|
PRX_CONTEXT RxContext)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PDFS_NAME_CONTEXT pDfsNameContext;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
BOOLEAN fNewServerEntry;
|
|
|
|
UNICODE_STRING ServerName;
|
|
|
|
if ((RxContext == NULL) ||
|
|
(RxContext->CurrentIrp == NULL) ||
|
|
(RxContext->CurrentIrpSp->MajorFunction != IRP_MJ_CREATE)) {
|
|
return Status;
|
|
}
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(
|
|
RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext == NULL) {
|
|
return Status;
|
|
}
|
|
|
|
Status = CscDfsParseDfsPath(
|
|
&pDfsNameContext->UNCFileName,
|
|
&ServerName,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return Status;
|
|
}
|
|
|
|
if (!fShadow)
|
|
{
|
|
ASSERT(FALSE);
|
|
}
|
|
// Ensure that a server entry in the disconnected
|
|
// state is created
|
|
|
|
SmbCeAcquireResource();
|
|
|
|
pServerEntry = SmbCeFindServerEntry(
|
|
&ServerName,
|
|
SMBCEDB_FILE_SERVER,
|
|
NULL);
|
|
|
|
if (pServerEntry == NULL) {
|
|
Status = SmbCeFindOrConstructServerEntry(
|
|
&ServerName,
|
|
SMBCEDB_FILE_SERVER,
|
|
&pServerEntry,
|
|
&fNewServerEntry,
|
|
NULL);
|
|
if (pServerEntry && fNewServerEntry)
|
|
{
|
|
pServerEntry->Server.IsFakeDfsServerForOfflineUse = TRUE;
|
|
// DbgPrint(
|
|
// "CscPrepareDfsServerEntryForDisconnectedOperation: 0x%x [%wZ] is a FAKE DFS entry\n",
|
|
// pServerEntry,
|
|
// &ServerName);
|
|
}
|
|
} else {
|
|
if (pServerEntry == pCurrentServerEntry) {
|
|
// The find routine references the server entry.
|
|
// If this happens to be the same as the current server
|
|
// entry then the appropriate referencing for disconnected
|
|
// operaton has already been done,
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
}
|
|
}
|
|
|
|
if (pServerEntry != NULL) {
|
|
// DbgPrint("CscPrepareDfsServerEntry %wZ \n", &pServerEntry->Name);
|
|
InterlockedExchange(
|
|
&pServerEntry->Server.CscState,
|
|
ServerCscDisconnected);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
SmbCeReleaseResource();
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = CscGrabPathFromDfs(
|
|
RxContext->CurrentIrpSp->FileObject,
|
|
pDfsNameContext);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
CscTransitionVNetRootForDisconnectedOperation(
|
|
PRX_CONTEXT RxContext,
|
|
PMRX_V_NET_ROOT pVNetRoot,
|
|
NTSTATUS RemoteStatus)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine transitions the server entry for disconnected mode of
|
|
operation
|
|
|
|
Arguments:
|
|
|
|
pVNetRoot -- the net root instance
|
|
|
|
RemoteStatus -- the failed status of the remote operation
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
If this routine returns STATUS_RETRY it implies that the associated server
|
|
entry has been successfully tranisitioned for disconnected operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status,ReturnStatus;
|
|
PMRX_FOBX capFobx = NULL;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext = NULL;
|
|
PSMBCEDB_NET_ROOT_ENTRY pNetRootEntry;
|
|
|
|
if(!MRxSmbIsCscEnabled || !fShadow) {
|
|
return(RemoteStatus);
|
|
}
|
|
|
|
// Notify the CSC agent of any transport changes if required
|
|
CscNotifyAgentOfNetStatusChangeIfRequired(FALSE);
|
|
|
|
ReturnStatus = RemoteStatus;
|
|
|
|
if (!CscTransitnOKToGoOffline(RemoteStatus)) {
|
|
return RemoteStatus;
|
|
}
|
|
|
|
if (pVNetRoot != NULL) {
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(pVNetRoot);
|
|
}
|
|
|
|
SmbCeLog(("CSCTrVNR %x VNR\n", pVNetRootContext));
|
|
SmbLog(LOG,
|
|
CscTransitionVNetRootForDisconnectedOperation_1,
|
|
LOGPTR(pVNetRootContext));
|
|
|
|
if (pVNetRootContext == NULL ||
|
|
pVNetRootContext->pServerEntry->Server.IsLoopBack) {
|
|
return RemoteStatus;
|
|
}
|
|
|
|
if (RxContext != NULL) {
|
|
capFobx = RxContext->pFobx;
|
|
}
|
|
|
|
pNetRootEntry = pVNetRootContext->pNetRootEntry;
|
|
|
|
|
|
|
|
if (!FlagOn(
|
|
pVNetRootContext->Flags,
|
|
SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE) &&
|
|
(pNetRootEntry != NULL) &&
|
|
(pNetRootEntry->NetRoot.NetRootType == NET_ROOT_DISK ||
|
|
pNetRootEntry->NetRoot.NetRootType == NET_ROOT_WILD)) {
|
|
|
|
if (pNetRootEntry->NetRoot.CscFlags != SMB_CSC_NO_CACHING) {
|
|
BOOLEAN TransitionVNetRoot;
|
|
UNICODE_STRING ServerName;
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
ULONG uFlags = DFS_FLAG_LAST_ALTERNATE;
|
|
|
|
TransitionVNetRoot = TRUE;
|
|
|
|
if ((capFobx != NULL) &&
|
|
(capFobx->pSrvOpen != NULL)) {
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(capFobx->pSrvOpen);
|
|
|
|
if ((pVNetRootContext->pServerEntry->Server.Version -
|
|
smbSrvOpen->Version) > 1) {
|
|
TransitionVNetRoot = FALSE;
|
|
}
|
|
}
|
|
|
|
if (TransitionVNetRoot) {
|
|
PDFS_NAME_CONTEXT pDfsNameContext = NULL;
|
|
ULONG uFlags = DFS_FLAG_LAST_ALTERNATE;
|
|
if (RxContext &&
|
|
RxContext->CurrentIrpSp &&
|
|
RxContext->CurrentIrpSp->MajorFunction == IRP_MJ_CREATE) {
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext)
|
|
{
|
|
uFlags = pDfsNameContext->Flags;
|
|
}
|
|
}
|
|
|
|
SmbCeLog(("CSCTrVNR DfsFlgs %x\n", uFlags));
|
|
SmbLog(LOG,
|
|
CscTransitionVNetRootForDisconnectedOperation_2,
|
|
LOGULONG(uFlags));
|
|
|
|
Status = CscpTransitionServerEntryForDisconnectedOperation(
|
|
RxContext,
|
|
pVNetRootContext->pServerEntry,
|
|
pNetRootEntry,
|
|
RemoteStatus,
|
|
FALSE, // to autodial or not to autodial
|
|
uFlags
|
|
);
|
|
|
|
// If the DFS share is in the database and the agent says it is OK to go disconnected
|
|
// then we want to create a DFS server entry and put that one in
|
|
// disconnected state too
|
|
|
|
if ((Status == STATUS_SUCCESS) &&
|
|
((pDfsNameContext != NULL)||(pNetRootEntry->NetRoot.DfsAware))) {
|
|
|
|
// DbgPrint("CSCTransitionVNETroot: Transitioning %wZ \n", &pVNetRootContext->pServerEntry->Name);
|
|
SmbCeLog(("CSCTrVNR try Tr %wZ \n", &pVNetRootContext->pServerEntry->Name));
|
|
SmbLog(LOG,
|
|
CscTransitionVNetRootForDisconnectedOperation_3,
|
|
LOGUSTR(pVNetRootContext->pServerEntry->Name));
|
|
|
|
Status = CscPrepareDfsServerEntryForDisconnectedOperation(
|
|
pVNetRootContext->pServerEntry,
|
|
RxContext);
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
ReturnStatus = Status;
|
|
} else {
|
|
ReturnStatus = STATUS_RETRY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ReturnStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscTransitionServerEntryForDisconnectedOperation(
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry,
|
|
PRX_CONTEXT RxContext,
|
|
NTSTATUS RemoteStatus,
|
|
BOOLEAN AutoDialRequired)
|
|
{
|
|
NTSTATUS TransitionStatus = STATUS_SUCCESS;
|
|
PMRX_V_NET_ROOT pVNetRoot = NULL;
|
|
|
|
ULONG uFlags = DFS_FLAG_LAST_ALTERNATE;
|
|
|
|
SmbCeLog(("CSCTrSvr IN %x %x %x %x\n", pServerEntry, RxContext, RemoteStatus, AutoDialRequired));
|
|
SmbLog(LOG,
|
|
CscTransitionServerEntryForDisconnectedOperation_1,
|
|
LOGPTR(pServerEntry)
|
|
LOGPTR(RxContext)
|
|
LOGULONG(RemoteStatus)
|
|
LOGUCHAR(AutoDialRequired)
|
|
LOGUSTR(pServerEntry->Name));
|
|
|
|
if ((RxContext != NULL) &&
|
|
(RxContext->CurrentIrp != NULL) &&
|
|
(RxContext->CurrentIrpSp->MajorFunction == IRP_MJ_CREATE)) {
|
|
PDFS_NAME_CONTEXT pDfsNameContext;
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(
|
|
RxContext->Create.NtCreateParameters.DfsNameContext);
|
|
|
|
if (pDfsNameContext != NULL) {
|
|
uFlags = pDfsNameContext->Flags;
|
|
SmbCeLog(("CSCTrSvr DFSFlgs %x\n", uFlags));
|
|
SmbLog(LOG,
|
|
CscTransitionServerEntryForDisconnectedOperation_2,
|
|
LOGULONG(uFlags));
|
|
}
|
|
}
|
|
|
|
if ((RxContext != NULL) &&
|
|
(RxContext->pFobx != NULL) &&
|
|
(RxContext->pFobx->pSrvOpen != NULL)) {
|
|
pVNetRoot = RxContext->pFobx->pSrvOpen->pVNetRoot;
|
|
}
|
|
|
|
if (pVNetRoot != NULL) {
|
|
TransitionStatus =
|
|
CscTransitionVNetRootForDisconnectedOperation(
|
|
RxContext,
|
|
pVNetRoot,
|
|
pServerEntry->ServerStatus);
|
|
} else {
|
|
TransitionStatus =
|
|
CscpTransitionServerEntryForDisconnectedOperation(
|
|
RxContext,
|
|
pServerEntry,
|
|
NULL,
|
|
RemoteStatus,
|
|
AutoDialRequired,
|
|
uFlags
|
|
);
|
|
|
|
if ((TransitionStatus == STATUS_SUCCESS) &&
|
|
(RxContext != NULL)) {
|
|
BOOLEAN TransitionDfsVNetRoot = FALSE;
|
|
|
|
TransitionDfsVNetRoot =
|
|
CscIsThisDfsCreateOperationTransitionableForDisconnectedOperation(
|
|
RxContext);
|
|
|
|
if (TransitionDfsVNetRoot) {
|
|
// DbgPrint("CSCTransitionServerEntry: Transitioning DFS server for ServerEntry %x \n", pServerEntry);
|
|
|
|
TransitionStatus = CscPrepareDfsServerEntryForDisconnectedOperation(
|
|
pServerEntry,
|
|
RxContext);
|
|
}
|
|
}
|
|
}
|
|
// Pulse the fill thread so it will start 10-min tries to reconnect
|
|
// It will go back to sleep if it succeeds
|
|
// DbgPrint("###CSCTransitionServerEntry: pulsing fill event\n");
|
|
MRxSmbCscSignalFillAgent(NULL, 0);
|
|
|
|
SmbCeLog(("CSCTrSvr Out %x\n", TransitionStatus));
|
|
SmbLog(LOG,
|
|
CscTransitionServerEntryForDisconnectedOperation_3,
|
|
LOGULONG(TransitionStatus));
|
|
|
|
return TransitionStatus;
|
|
}
|
|
|
|
BOOLEAN
|
|
CscPerformOperationInDisconnectedMode(
|
|
PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine detects if the operation should be performed in a disconnected
|
|
mode. Additionally if the operation needs to be performed in a disconnected
|
|
mode it prepares the open accordingly.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the Wrapper context for the operation
|
|
|
|
Return Value:
|
|
|
|
TRUE -- if the operation needs to be performed in the disconnected mode
|
|
and FALSE otherwise
|
|
|
|
Notes:
|
|
|
|
There are certain opens that are deferred by the SMB mini redirector in
|
|
the connected mode. These modes need to be evaluated when the transition is
|
|
made to disconnected mode, since in disconnected mode there are no
|
|
deferred opens.
|
|
|
|
The appropriate buffering change requests need to be done as well (TBI)
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
|
|
PMRX_SRV_OPEN SrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
BOOLEAN PerformOperationInDisconnectedMode = FALSE;
|
|
|
|
if(!MRxSmbIsCscEnabled) {
|
|
return(FALSE);
|
|
}
|
|
|
|
SrvOpen = RxContext->pRelevantSrvOpen;
|
|
|
|
// check if SrvOpen is NULL. This could happen if a mailslot operation was passed in here
|
|
if (!SrvOpen)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
if (FlagOn(smbSrvOpen->Flags, SMB_SRVOPEN_FLAG_LOCAL_OPEN)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(capFobx->pSrvOpen->pVNetRoot);
|
|
|
|
if (SmbCeIsServerInDisconnectedMode(pServerEntry) &&
|
|
!FlagOn(
|
|
pVNetRootContext->Flags,
|
|
SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE)) {
|
|
PMRX_SMB_FCB smbFcb;
|
|
|
|
smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
if (smbFcb->hShadow == 0) {
|
|
BOOLEAN FcbAcquired;
|
|
BOOLEAN PreviouslyAcquiredShared = FALSE;
|
|
|
|
// If the FCB resource has not been acquired, acquire it before
|
|
// performing the create.
|
|
|
|
if (!RxIsFcbAcquiredExclusive(capFcb)) {
|
|
if (RxIsFcbAcquiredShared(capFcb)) {
|
|
RxDbgTrace(0, Dbg, ("Shared holding condition detected for disconnected operation\n"));
|
|
RxReleaseFcbResourceInMRx(capFcb);
|
|
PreviouslyAcquiredShared = TRUE;
|
|
}
|
|
|
|
RxAcquireExclusiveFcbResourceInMRx(capFcb );
|
|
FcbAcquired = TRUE;
|
|
} else {
|
|
FcbAcquired = FALSE;
|
|
}
|
|
|
|
// This is a case of a deferred open for which the transition has
|
|
// been made to disconnected operation.
|
|
|
|
Status = MRxSmbDeferredCreate(RxContext);
|
|
|
|
//RxIndicateChangeOfBufferingState(
|
|
// capFcb->pNetRoot->pSrvCall,
|
|
// MRxSmbMakeSrvOpenKey(smbFcb->Tid,smbSrvOpen->Fid),
|
|
// (PVOID)2);
|
|
|
|
if (FcbAcquired) {
|
|
RxReleaseFcbResourceInMRx(capFcb);
|
|
}
|
|
|
|
if (PreviouslyAcquiredShared) {
|
|
RxAcquireSharedFcbResourceInMRx(capFcb );
|
|
}
|
|
}
|
|
|
|
PerformOperationInDisconnectedMode = TRUE;
|
|
}
|
|
|
|
return PerformOperationInDisconnectedMode;
|
|
}
|
|
|
|
#if 0
|
|
int
|
|
AllPinnedFilesFilled(
|
|
HSHARE hShare,
|
|
BOOL *lpfComplete
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
CSC_ENUMCOOKIE hPQ;
|
|
PQPARAMS sPQP;
|
|
int iRet = SRET_ERROR;
|
|
|
|
ASSERT(hShare);
|
|
ASSERT(lpfComplete);
|
|
|
|
|
|
// Open the priority q
|
|
if (!(hPQ = HBeginPQEnum()))
|
|
{
|
|
RxDbgTrace(0, Dbg, ("AllPinnedFilesFilled: Error opening Priority Q database\r\n"));
|
|
return SRET_ERROR;
|
|
}
|
|
|
|
*lpfComplete = TRUE;
|
|
memset(&sPQP, 0, sizeof(PQPARAMS));
|
|
sPQP.uEnumCookie = hPQ;
|
|
|
|
// go down the Q once
|
|
do
|
|
{
|
|
if(NextPriSHADOW(&sPQP) < SRET_OK)
|
|
{
|
|
RxDbgTrace(0, Dbg, ("AllPinnedFilesFilled: PQ record read error\r\n"));
|
|
goto bailout;
|
|
}
|
|
|
|
|
|
|
|
if (!sPQP.hShadow)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// see if any of the pinned files for the specific
|
|
// server is sparse
|
|
if ((hShare == sPQP.hShare)
|
|
&& (sPQP.ulStatus & SHADOW_IS_FILE) // It is a file
|
|
&& ((sPQP.ulHintPri || mPinFlags(sPQP.ulHintFlags)))// it is a pinned file
|
|
)
|
|
{
|
|
if (sPQP.ulStatus & SHADOW_SPARSE)
|
|
{
|
|
// we found a sparse file
|
|
*lpfComplete = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
while (sPQP.uPos);
|
|
|
|
iRet = SRET_OK;
|
|
|
|
bailout:
|
|
if (hPQ)
|
|
{
|
|
EndPQEnum(hPQ);
|
|
}
|
|
if (iRet == SRET_ERROR)
|
|
{
|
|
*lpfComplete = FALSE;
|
|
}
|
|
return (iRet);
|
|
}
|
|
#endif
|
|
|
|
BOOLEAN
|
|
CscGetServerNameWaitingToGoOffline(
|
|
OUT PWCHAR ServerName,
|
|
IN OUT LPDWORD lpdwBufferSize,
|
|
OUT NTSTATUS *lpStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This routine returns the name of the server which has asked the agent to
|
|
throw a popup to the user.
|
|
Arguments:
|
|
ServerName returns the name of the server
|
|
Return Value:
|
|
Fails if no server is waiting for the popup to comeback
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN fRet = FALSE;
|
|
DWORD dwSize = *lpdwBufferSize;
|
|
*lpStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
if (CscServerEntryBeingTransitioned)
|
|
{
|
|
PWCHAR Name;
|
|
ULONG NameLength;
|
|
|
|
if (!CscDfsRootServerEntryBeingTransitioned) {
|
|
NameLength = CscServerEntryBeingTransitioned->Name.Length;
|
|
Name = CscServerEntryBeingTransitioned->Name.Buffer;
|
|
} else {
|
|
NameLength = CscDfsRootServerEntryBeingTransitioned->Name.Length;
|
|
Name = CscDfsRootServerEntryBeingTransitioned->Name.Buffer;
|
|
}
|
|
|
|
*lpdwBufferSize = (DWORD)(NameLength+2+2);
|
|
|
|
if(dwSize >= (DWORD)(NameLength+2+2))
|
|
{
|
|
*ServerName='\\';
|
|
|
|
memcpy(
|
|
ServerName+1,
|
|
Name,
|
|
NameLength);
|
|
|
|
memset(((LPBYTE)(ServerName+1))+NameLength, 0, 2);
|
|
|
|
*lpStatus = STATUS_SUCCESS;
|
|
fRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
*lpStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
BOOLEAN
|
|
CscShareIdToShareName(
|
|
IN ULONG hShare,
|
|
OUT PWCHAR ShareName,
|
|
IN OUT LPDWORD lpdwBufferSize,
|
|
OUT NTSTATUS *lpStatus
|
|
)
|
|
{
|
|
SHAREREC sSR;
|
|
DWORD dwSize = *lpdwBufferSize;
|
|
ULONG NameLength;
|
|
INT iRet;
|
|
|
|
// DbgPrint("CscShareIdToShareName(%d)\n", hShare);
|
|
|
|
*lpStatus = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
if (hShare == 0)
|
|
goto AllDone;
|
|
|
|
EnterShadowCrit();
|
|
iRet = GetShareRecord(lpdbShadow, hShare, &sSR);
|
|
LeaveShadowCrit();
|
|
if (iRet >= 0 && sSR.uchType == (UCHAR)REC_DATA) {
|
|
NameLength = (wcslen(sSR.rgPath)+1) * sizeof(WCHAR);
|
|
*lpdwBufferSize = (DWORD)NameLength;
|
|
if(dwSize >= (DWORD)(NameLength)) {
|
|
memset(ShareName, 0, dwSize);
|
|
if (NameLength > 0)
|
|
memcpy(ShareName, sSR.rgPath, NameLength);
|
|
*lpStatus = STATUS_SUCCESS;
|
|
} else {
|
|
*lpStatus = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
AllDone:
|
|
// DbgPrint("CscShareIdToShareName exit 0x%x\n", *lpStatus);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
CSCCheckForAcd(VOID)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING nameString;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
PFILE_OBJECT pAcdFileObject;
|
|
PDEVICE_OBJECT pAcdDeviceObject;
|
|
BOOLEAN fAutoDialON=FALSE;
|
|
PIRP pIrp;
|
|
|
|
//
|
|
// Initialize the name of the automatic
|
|
// connection device.
|
|
//
|
|
RtlInitUnicodeString(&nameString, ACD_DEVICE_NAME);
|
|
//
|
|
// Get the file and device objects for the
|
|
// device.
|
|
//
|
|
status = IoGetDeviceObjectPointer(
|
|
&nameString,
|
|
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
|
|
&pAcdFileObject,
|
|
&pAcdDeviceObject);
|
|
if (status != STATUS_SUCCESS)
|
|
{
|
|
RxDbgTrace(0, Dbg, ("CSCCheckForAcd: failed with status=%x \r\n", status));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Reference the device object.
|
|
//
|
|
ObReferenceObject(pAcdDeviceObject);
|
|
//
|
|
// Remove the reference IoGetDeviceObjectPointer()
|
|
// put on the file object.
|
|
//
|
|
ObDereferenceObject(pAcdFileObject);
|
|
|
|
pIrp = IoBuildDeviceIoControlRequest(
|
|
IOCTL_INTERNAL_ACD_QUERY_STATE,
|
|
pAcdDeviceObject,
|
|
NULL,
|
|
0,
|
|
&fAutoDialON,
|
|
sizeof(fAutoDialON),
|
|
TRUE,
|
|
NULL,
|
|
&ioStatusBlock);
|
|
if (pIrp == NULL) {
|
|
ObDereferenceObject(pAcdDeviceObject);
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Submit the request to the
|
|
// automatic connection driver.
|
|
//
|
|
status = IoCallDriver(pAcdDeviceObject, pIrp);
|
|
|
|
ObDereferenceObject(pAcdDeviceObject);
|
|
return fAutoDialON;
|
|
}
|
|
|
|
BOOLEAN
|
|
CscTransitnOKToGoOffline(
|
|
NTSTATUS RemoteStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
|
|
switch (RemoteStatus) {
|
|
case STATUS_CONNECTION_DISCONNECTED:
|
|
case STATUS_IO_TIMEOUT:
|
|
case STATUS_NETWORK_UNREACHABLE:
|
|
case STATUS_BAD_NETWORK_NAME:
|
|
case STATUS_BAD_NETWORK_PATH:
|
|
case STATUS_NETWORK_NAME_DELETED:
|
|
return TRUE;
|
|
default :
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
CscIsSpecialShare(
|
|
PUNICODE_STRING ShareName)
|
|
{
|
|
ULONG i;
|
|
BOOLEAN fSpecial = FALSE;
|
|
|
|
// DbgPrint("CscIsSpecialShare(%wZ)\n", ShareName);
|
|
for (i = 0;
|
|
(i < (sizeof(CscSpecialShares) / sizeof(CscSpecialShares[0]))) &&
|
|
!fSpecial;
|
|
i++) {
|
|
if (CscSpecialShares[i].Length == ShareName->Length) {
|
|
if (_wcsnicmp(
|
|
CscSpecialShares[i].Buffer,
|
|
ShareName->Buffer,
|
|
ShareName->Length/sizeof(WCHAR)) == 0) {
|
|
fSpecial = TRUE;
|
|
}
|
|
}
|
|
}
|
|
// DbgPrint("CscIsSpecialShare returning %d\n", fSpecial);
|
|
return fSpecial;
|
|
}
|