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

3134 lines
85 KiB
C

//+----------------------------------------------------------------------------
//
// Copyright (C) 1996, Microsoft Corporation
//
// File: srv.c
//
// Contents: Support for interacting with the SMB server.
//
// Classes: None
//
// Functions: DfsSrvFsctrl
// DfsFsctrlTranslatePath
// DfsFsctrlGetReferrals
// DfsFsctrlIsShareInDfs
//
//-----------------------------------------------------------------------------
#include "dfsprocs.h"
#include "srv.h"
#include "smbtypes.h"
#include "smbtrans.h"
#include "know.h"
#include "ftdfs.h"
#include "sitesup.h"
#include "ipsup.h"
#include "spcsup.h"
#include "dfslpc.h"
#include "fsctrl.h"
#include "dfswml.h"
#include <ntlsa.h>
#include <netevent.h>
#define Dbg DEBUG_TRACE_REFERRALS
#define MAXIMUM_DFS_REFERRALS 4096
DFS_IPADDRESS ZeroIpAddress = {0};
NTSTATUS
DfspFormPrefix(
IN PDFS_LOCAL_VOL_ENTRY LvEntry,
IN PUNICODE_STRING ParentPath,
IN PUNICODE_STRING DfsPathName,
IN OUT PUNICODE_STRING Prefix);
ULONG
DfspGetV1ReferralSize(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName);
ULONG
DfspGetV2ReferralSize(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName);
ULONG
DfspGetV3ReferralSize(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName);
VOID
DfspGetV1Referral(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName,
OUT PRESP_GET_DFS_REFERRAL Ref);
NTSTATUS
DfspGetV2Referral(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName,
IN ULONG BufferSize,
OUT PRESP_GET_DFS_REFERRAL Ref,
OUT PULONG ReferralSize);
NTSTATUS
DfspGetV3Referral(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName,
IN PDFS_IP_INFO pIpInfo,
IN ULONG BufferSize,
OUT PRESP_GET_DFS_REFERRAL Ref,
OUT PULONG ReferralSize);
NTSTATUS
DfspGetOneV3SpecialReferral(
IN PDFS_SPECIAL_INFO pSpcInfo,
IN PUNICODE_STRING Name,
IN PDFS_IP_INFO pIpInfo,
OUT PRESP_GET_DFS_REFERRAL Ref,
IN ULONG MaximumSize,
PULONG ReturnedSize);
NTSTATUS
DfspGetAllV3SpecialReferral(
IN PDFS_IP_INFO pIpInfo,
OUT PRESP_GET_DFS_REFERRAL Ref,
IN ULONG MaximumSize,
PULONG ReturnedSize);
NTSTATUS
DfspGetV3FtDfsReferral(
IN PUNICODE_STRING DomainName,
IN PUNICODE_STRING ShareName,
IN PDFS_IP_INFO pIpInfo,
OUT PRESP_GET_DFS_REFERRAL Ref,
IN ULONG MaximumSize,
PULONG ReturnedSize);
NTSTATUS
DfsFsctrlTranslatePath(
IN PVOID InputBuffer,
IN ULONG InputBufferLength);
NTSTATUS
DfsFsctrlGetReferrals(
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG ReturnedSize);
NTSTATUS
DfsFsctrlIsShareInDfs(
IN PVOID InputBuffer,
IN ULONG InputBufferLength);
NTSTATUS
DfsFsctrlFindShare(
IN PVOID InputBuffer,
IN ULONG InputBufferLength);
VOID
SrvShuffle(
PDFS_REFERRAL_LIST pRefList,
LONG nStart,
LONG nEnd);
ULONG
DfsIpOrdering(
IN PDFS_IP_INFO pIpInfo,
IN ULONG RefCount,
IN PDFS_REFERRAL_LIST pRefList);
PDFS_SPECIAL_INFO
DfspLookupSpcEntry(
IN PUNICODE_STRING SpecialName);
BOOLEAN
DfspIsSpecialShare(
PUNICODE_STRING ShareName);
#define UNICODE_STRING_STRUCT(s) \
{sizeof(s) - sizeof(WCHAR), sizeof(s) - sizeof(WCHAR), (s)}
static UNICODE_STRING SpecialShares[] = {
UNICODE_STRING_STRUCT(L"SYSVOL"),
UNICODE_STRING_STRUCT(L"NETLOGON"),
UNICODE_STRING_STRUCT(L"DEBUG")
};
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, DfsSrvFsctrl )
#pragma alloc_text( PAGE, DfsFsctrlTranslatePath )
#pragma alloc_text( PAGE, DfsFsctrlGetReferrals )
#pragma alloc_text( PAGE, DfsFsctrlIsShareInDfs )
#pragma alloc_text( PAGE, DfsFsctrlFindShare )
#pragma alloc_text( PAGE, DfsIpOrdering )
#pragma alloc_text( PAGE, DfspFormPrefix )
#pragma alloc_text( PAGE, DfspGetV1ReferralSize )
#pragma alloc_text( PAGE, DfspGetV2ReferralSize )
#pragma alloc_text( PAGE, DfspGetV3ReferralSize )
#pragma alloc_text( PAGE, DfspGetV1Referral )
#pragma alloc_text( PAGE, DfspGetV2Referral )
#pragma alloc_text( PAGE, DfspGetV3Referral )
#pragma alloc_text( PAGE, DfspGetAllV3SpecialReferral )
#pragma alloc_text( PAGE, DfspGetOneV3SpecialReferral )
#pragma alloc_text( PAGE, DfspLookupSpcEntry )
#endif // ALLOC_PRAGMA
//+----------------------------------------------------------------------------
//
// Function: DfsSrvFsctrl
//
// Synopsis: Process fsctrls used by the server to communicate with Dfs.
//
// Arguments: [IoControlCode] -- The fsctrl code.
// [InputBuffer] -- Input buffer for fsctrl.
// [InputBufferLength] -- Length in bytes of InputBuffer
// [OutputBuffer] -- Output buffer for fsctrl.
// [OutputBufferLength] -- Length in bytes of OutputBuffer
// [IoStatus] -- place to return status of fsctrl.
//
// Returns: [STATUS_SUCCESS] -- Fsctrl handled successfully.
//
// [STATUS_INVALID_DEVICE_REQUEST] -- Unrecognized fsctrl code.
//
// Status returned by the appropriate fsctrl handler.
//
//-----------------------------------------------------------------------------
VOID DfsSrvFsctrl(
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PIO_STATUS_BLOCK IoStatus)
{
KPROCESSOR_MODE PreviousMode;
ULONG returnedSize = 0;
if (IoControlCode == FSCTL_DFS_IS_ROOT) {
if (DfsData.MachineState == DFS_ROOT_SERVER) {
IoStatus->Status = STATUS_SUCCESS;
} else {
IoStatus->Status = STATUS_PATH_NOT_COVERED;
DFS_TRACE_HIGH(ERROR, DfsSrvFsctrl_Error1, LOGSTATUS(IoStatus->Status));
}
return;
} else if (IoControlCode == FSCTL_DFS_IS_SHARE_IN_DFS) {
IoStatus->Status = DfsFsctrlIsShareInDfs(
InputBuffer,
InputBufferLength);
return;
}
PreviousMode = ExGetPreviousMode();
if (PreviousMode != KernelMode) {
IoStatus->Status = STATUS_INVALID_PARAMETER;
DFS_TRACE_HIGH(ERROR, DfsSrvFsctrl_Error2, LOGSTATUS(IoStatus->Status));
return;
}
switch (IoControlCode) {
case FSCTL_DFS_TRANSLATE_PATH:
IoStatus->Status = DfsFsctrlTranslatePath(
InputBuffer,
InputBufferLength);
break;
case FSCTL_DFS_GET_REFERRALS:
IoStatus->Status = DfsFsctrlGetReferrals(
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength,
&returnedSize);
IoStatus->Information = returnedSize;
break;
case FSCTL_DFS_REPORT_INCONSISTENCY:
IoStatus->Status = STATUS_SUCCESS;
break;
case FSCTL_DFS_FIND_SHARE:
IoStatus->Status = DfsFsctrlFindShare(
InputBuffer,
InputBufferLength);
break;
default:
IoStatus->Status = STATUS_INVALID_DEVICE_REQUEST;
DFS_TRACE_HIGH(ERROR, DfsSrvFsctrl_Error3, LOGSTATUS(IoStatus->Status));
break;
}
}
//+----------------------------------------------------------------------------
//
// Function: DfsFsctrlTranslatePath
//
// Synopsis: Given a share path of the form \??\X:\foo\bar
// and a full-blown Dfs prefix of the form \Root\Dfs\frum\bump,
// this routine will determine the path relative to
// \??\X:\foo\bar that maps to \Root\Dfs\frum\bump.
//
// In the above example, if \??\X:\foo\bar maps to
// \Root\Dfs\frum, then this routine will return \bump.
//
// Since this fsctrl is intended for use by the server, the
// input parameter \Root\Dfs\frum\bump will itself be truncated
// to indicate the relative path.
//
// Arguments: [InputBuffer] -- Pointer to DFS_TRANSLATE_PATH_ARG.
//
// [InputBufferLength] -- Length in bytes of InputBuffer
//
// Returns: [STATUS_SUCCESS] -- Input path successfully translated.
//
// [STATUS_PATH_NOT_COVERED] -- The input path crossed a junction
// point, or is not in the dfs at all.
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsFsctrlTranslatePath(
IN PVOID InputBuffer,
IN ULONG InputBufferLength)
{
PDFS_TRANSLATE_PATH_ARG arg = (PDFS_TRANSLATE_PATH_ARG) InputBuffer;
PDFS_PKT pkt;
PDFS_PKT_ENTRY pktEntry, shortPktEntry;
PUNICODE_PREFIX_TABLE_ENTRY lvPrefix;
PDFS_LOCAL_VOL_ENTRY lvEntry;
UNICODE_STRING lvName;
WCHAR nameBuffer[MAX_PATH];
UNICODE_STRING prefix;
UNICODE_STRING lastComponent;
UNICODE_STRING remPath, shortRemPath;
NTSTATUS status = STATUS_SUCCESS;
DebugTrace(+1, Dbg, "DfsFsctrlTranslatePath entered\n", 0);
DFS_TRACE_NORM(EVENT, DfsFsctrlTranslatePath_Start,
LOGSTATUS(status)
LOGUSTR(arg->SubDirectory)
LOGUSTR(arg->DfsPathName)
LOGUSTR(arg->ParentPathName));
lvName = arg->SubDirectory;
DebugTrace( 0, Dbg, "lvName=%wZ\n", &lvName);
RtlZeroMemory( &prefix, sizeof(prefix) );
//
// Lookup the localVol in the local volume prefix table.
//
pkt = _GetPkt();
PktAcquireExclusive( pkt, TRUE );
lvPrefix = DfsFindUnicodePrefix(
&pkt->LocalVolTable,
&lvName,
&remPath);
if (lvPrefix != NULL) {
DebugTrace( 0, Dbg, "found lvPrefix=0x%x\n", lvPrefix);
lvEntry = CONTAINING_RECORD(
lvPrefix,
DFS_LOCAL_VOL_ENTRY,
PrefixTableEntry);
if (lvEntry->LocalPath.Length == lvName.Length) {
DebugTrace( 0, Dbg, "found path for a local vol\n", 0);
DebugTrace( 0, Dbg, "lvEntry->LocalPath=%wZ\n", &lvEntry->LocalPath);
//
// Found an exact match for a local volume. Next, we check
// whether the Dfs path crosses an exit point.
//
prefix.Length = 0;
prefix.MaximumLength = sizeof( nameBuffer );
prefix.Buffer = nameBuffer;
status = DfspFormPrefix(
lvEntry,
&arg->ParentPathName,
&arg->DfsPathName,
&prefix);
if (arg->Flags & DFS_TRANSLATE_STRIP_LAST_COMPONENT) {
DebugTrace( 0, Dbg, "prefix=%wZ\n", &prefix);
lastComponent = prefix;
RemoveLastComponent( &lastComponent, &prefix );
lastComponent.Length -= prefix.Length;
lastComponent.MaximumLength -= prefix.Length;
lastComponent.Buffer += (prefix.Length / sizeof(WCHAR));
DebugTrace( 0, Dbg, "lastComponent=%wZ\n", &lastComponent);
}
if (NT_SUCCESS(status)) {
pktEntry = PktLookupEntryByPrefix(pkt, &prefix, &remPath);
shortPktEntry =
PktLookupEntryByShortPrefix(pkt, &prefix, &shortRemPath);
if (shortPktEntry != NULL) {
if (pktEntry == NULL ||
shortPktEntry->Id.Prefix.Length >
pktEntry->Id.Prefix.Length) {
pktEntry = shortPktEntry;
remPath = shortRemPath;
}
}
DebugTrace( 0, Dbg, "pktEntry=0x%x\n", pktEntry);
if (pktEntry != NULL) {
//
// Found a pkt entry that matches. Is it the one belonging
// to the local volume that matched?
//
if (pktEntry == lvEntry->PktEntry) {
DebugTrace( 0, Dbg, "pktEntry===lvEntryPktEntry\n", 0);
ASSERT( pktEntry->LocalService != NULL );
//
// For DfsPathName which is relative to some parent
// path, we don't need to adjust the DfsPathName;
// simply checking to see if we landed up with the
// same Pkt Entry is all that is needed.
//
if (arg->ParentPathName.Length == 0) {
if (arg->Flags & DFS_TRANSLATE_STRIP_LAST_COMPONENT) {
DebugTrace( 0, Dbg, "STRIP_LAST on \n", 0);
remPath.Length += lastComponent.Length;
}
arg->DfsPathName.Length = remPath.Length;
RtlMoveMemory(
arg->DfsPathName.Buffer,
remPath.Buffer,
remPath.Length);
arg->DfsPathName.Buffer[
remPath.Length/sizeof(WCHAR)] = UNICODE_NULL;
DebugTrace( 0, Dbg, "arg->DfsPathName=%wZ\n", &arg->DfsPathName);
}
status = STATUS_SUCCESS;
} else {
//
// Must have crossed a junction point (to another local
// volume!)
//
status = STATUS_PATH_NOT_COVERED;
}
} else {
//
// We don't know anything about this Dfs path. The client
// must be guessing...
//
status = STATUS_PATH_NOT_COVERED;
}
}
} else {
//
// Didn't find an exact match for the given local volume name.
//
status = STATUS_PATH_NOT_COVERED;
}
} else {
//
// Didn't find a match for the given local volume at all!
//
status = STATUS_PATH_NOT_COVERED;
}
PktRelease( pkt );
if (prefix.Buffer != NULL && prefix.Buffer != nameBuffer) {
ExFreePool( prefix.Buffer );
}
DebugTrace( 0, Dbg, "DfsFsctrlTranslatePath exit 0x%x\n", ULongToPtr( status ));
DebugTrace( 0, Dbg, "-->arg->SubDirectory=%wZ\n", &arg->SubDirectory);
DebugTrace( 0, Dbg, "-->arg->ParentPathName=%wZ\n", &arg->ParentPathName);
DebugTrace(-1, Dbg, "-->arg->DfsPathName=%wZ\n", &arg->DfsPathName);
DFS_TRACE_ERROR_HIGH(status, ALL_ERROR, DfsFsctrlTranslatePath_Error1,
LOGSTATUS(status)
LOGUSTR(arg->SubDirectory)
LOGUSTR(arg->ParentPathName)
LOGUSTR(arg->DfsPathName));
DFS_TRACE_NORM(EVENT, DfsFsctrlTranslatePath_End,
LOGSTATUS(status)
LOGUSTR(arg->SubDirectory)
LOGUSTR(arg->DfsPathName)
LOGUSTR(arg->ParentPathName));
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfspFormPrefix
//
// Synopsis: Helper routine that forms the arguments to
// DfsFsctrlTranslatePath into a prefix appropriate for looking
// up in the Pkt.
//
// Arguments: [LvEntry] -- The local volume entry on which the ParentPath
// was opened.
// [ParentPath] -- The path of the parent object, relative to
// the dfs volume referenced by LvEntry. (equivalently,
// it is also relative to the Share Path). If this is a 0
// length string, then this is not a relative open.
// [DfsPathName] -- The name of the file that is being translated.
// [Prefix] -- The fully formed prefix is returned here. On
// entry, this should be initialized to have a buffer of
// some reasonable size; on return, the buffer might be
// replaced with a bigger one if needed, in which case
// the buffer should be freed using ExFreePool.
//
// Returns: [STATUS_SUCCESS] -- Successfully formed prefix.
//
// [STATUS_INSUFFICIENT_RESOURCES] -- Unable to form prefix.
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspFormPrefix(
IN PDFS_LOCAL_VOL_ENTRY LvEntry,
IN PUNICODE_STRING ParentPath,
IN PUNICODE_STRING DfsPathName,
IN OUT PUNICODE_STRING Prefix)
{
NTSTATUS status;
ULONG sizeRequired;
DebugTrace(+1, Dbg, "DfspFormPrefix(LvEntry=0x%x)\n", LvEntry);
DebugTrace( 0, Dbg, " ParentPath=%wZ\n", ParentPath);
DebugTrace( 0, Dbg, " DfsPathName=%wZ\n", DfsPathName);
ASSERT(Prefix->Buffer != NULL);
sizeRequired = sizeof(UNICODE_PATH_SEP) +
ParentPath->Length +
sizeof(UNICODE_PATH_SEP) +
DfsPathName->Length;
if (ParentPath->Length > 0) {
sizeRequired += LvEntry->PktEntry->Id.Prefix.Length;
}
if (sizeRequired > MAXUSHORT) {
status = STATUS_INSUFFICIENT_RESOURCES;
DFS_TRACE_HIGH(ERROR, DfspFormPrefix_Error1,
LOGSTATUS(status)
LOGUSTR(*ParentPath)
LOGUSTR(*DfsPathName));
return status;
}
if (sizeRequired > Prefix->MaximumLength) {
Prefix->MaximumLength = (USHORT)sizeRequired;
Prefix->Buffer = ExAllocatePoolWithTag(PagedPool, sizeRequired, ' sfD');
}
if (Prefix->Buffer != NULL) {
if (ParentPath->Length > 0) {
RtlAppendUnicodeStringToString(
Prefix,
&LvEntry->PktEntry->Id.Prefix);
DfsConcatenateFilePath(
Prefix,
ParentPath->Buffer,
(USHORT) (ParentPath->Length));
} else {
RtlAppendUnicodeToString(
Prefix,
UNICODE_PATH_SEP_STR);
}
DfsConcatenateFilePath(
Prefix,
DfsPathName->Buffer,
DfsPathName->Length);
status = STATUS_SUCCESS;
} else {
status = STATUS_INSUFFICIENT_RESOURCES;
}
DebugTrace( 0, Dbg, "on exit, Prefix=%wZ\n", Prefix);
DebugTrace(-1, Dbg, "DfspFormPrefix exit 0x%x\n", ULongToPtr( status ));
DFS_TRACE_ERROR_HIGH(status, ALL_ERROR, DfspFormPrefix_Error2,
LOGSTATUS(status)
LOGUSTR(*ParentPath)
LOGUSTR(*DfsPathName));
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfsFsctrlGetReferrals
//
// Synopsis: Returns a referral buffer for the given prefix.
//
// Arguments: [InputBuffer] -- Pointer to DFS_GET_REFERRALS_INPUT_ARG
// [InputBufferLength] -- Length of InputBuffer.
// [OutputBuffer] -- On successful return, contains a
// DFS_GET_REFERRALS_OUTPUT_BUFFER.
// [OutputBufferLength] -- Length in bytes of OutputBuffer.
// [ReturnedSize] -- On successful return, size of the returned
// referral. If returning STATUS_BUFFER_OVERFLOW, this
// variable contains the size of the buffer required to
// fit the entire referral.
//
// Returns: [STATUS_SUCCESS] -- DFS_GET_REFERRALS_OUTPUT_BUFFER
// successfully returned.
//
// [STATUS_BUFFER_OVERFLOW] -- Couldn't fit all the referrals
// in the buffer; the required buffer length is returned
// in ReturnedSize.
//
// [STATUS_NO_SUCH_DEVICE] -- Unable to find the Dfs volume in
// the local pkt.
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsFsctrlGetReferrals(
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength,
OUT PULONG ReturnedSize)
{
NTSTATUS status = STATUS_SUCCESS;
PDFS_GET_REFERRALS_INPUT_ARG arg =
(PDFS_GET_REFERRALS_INPUT_ARG) InputBuffer;
PRESP_GET_DFS_REFERRAL ref =
(PRESP_GET_DFS_REFERRAL) OutputBuffer;
PDFS_PKT pkt;
PDFS_PKT_ENTRY pktEntry, shortPfxEntry;
PDFS_IPADDRESS pIpAddress = NULL;
UNICODE_STRING prefix;
UNICODE_STRING PrefixTail;
UNICODE_STRING remPath;
UNICODE_STRING DomainName;
UNICODE_STRING ShareName;
ULONG i, size, maxLevel;
LPWSTR AltList;
PDFS_IP_INFO pIpInfo = NULL;
PDFS_SPECIAL_INFO pSpcInfo = NULL;
PSPECIAL_HASH_TABLE pHashTable = DfsData.SpcHashTable;
DFS_TRACE_NORM(EVENT, DfsFsctrlGetReferrals_Start,
LOGSTATUS(status)
LOGUSTR(arg->DfsPathName));
//
// If we're not started, then say so
//
if (DfsData.OperationalState != DFS_STATE_STARTED && DfsData.IsDC == FALSE) {
status = STATUS_DFS_UNAVAILABLE;
DFS_TRACE_HIGH(ERROR, DfsFsctrlGetReferrals_Error1, LOGSTATUS(status));
//return( status );
goto cleanup;
}
if (InputBufferLength == sizeof(DFS_GET_REFERRALS_INPUT_ARG)
&&
RtlCompareMemory(
&ZeroIpAddress,
&arg->IpAddress,
sizeof(DFS_IPADDRESS)) != sizeof(DFS_IPADDRESS)) {
pIpAddress = &arg->IpAddress;
}
prefix = arg->DfsPathName;
if ((maxLevel = arg->MaxReferralLevel) > 3)
maxLevel = 3;
DebugTrace(0, Dbg, "DfsFsctrlGetReferrals(maxLevel=%d)\n", ULongToPtr( maxLevel ));
// Create domain, sharename, remainder from prefix
remPath.Length = remPath.MaximumLength = 0;
DfspParsePath(&prefix, &DomainName, &ShareName, &remPath);
// See if this is a special name referral
if (DfsData.IsDC == TRUE) {
if (maxLevel >= 3 &&
(DomainName.Length == 0 ||
(DomainName.Length > 0 && ShareName.Length == 0))) {
DebugTrace(0, Dbg, "DfsFsctrlGetReferrals: case 1\n", 0);
ref->PathConsumed = 0;
if (DomainName.Length > 0) {
pSpcInfo = DfspLookupSpcEntry(&DomainName);
if (pSpcInfo != NULL && pSpcInfo->NameCount > 1) {
pIpInfo = DfsLookupSiteByIpaddress(pIpAddress, TRUE);
}
status = DfspGetOneV3SpecialReferral(
pSpcInfo,
&DomainName,
pIpInfo,
ref,
OutputBufferLength,
ReturnedSize);
if (pSpcInfo != NULL) {
DfsReleaseSpcInfo(pHashTable, pSpcInfo);
pSpcInfo = NULL;
}
} else {
status = DfspGetAllV3SpecialReferral(
pIpInfo,
ref,
OutputBufferLength,
ReturnedSize);
}
DfsReleaseIpInfo(pIpInfo);
if (DfsEventLog > 1)
LogWriteMessage(DFS_REFERRAL_REQUEST, status, 1, &prefix);
//return( status );
goto cleanup;
}
}
//
// If we are going to hand out regular referrals,
// check to see that we are a root server, and that the
// referral request looks good.
//
pSpcInfo = DfspLookupSpcEntry(&DomainName);
if (DfsData.MachineState == DFS_ROOT_SERVER
&&
DfsData.OperationalState == DFS_STATE_STARTED
&&
DfsData.LvState == LV_INITIALIZED
&&
DomainName.Length > 0
&&
ShareName.Length > 0
&&
(pSpcInfo == NULL || remPath.Length > 0)
) {
DebugTrace(0, Dbg, "DfsFsctrlGetReferrals: case 2\n", 0);
pkt = _GetPkt();
if (maxLevel >= 3) {
PktAcquireShared( pkt, TRUE );
pktEntry = PktLookupEntryByPrefix( pkt, &prefix, &remPath);
PktRelease( pkt );
if (pktEntry != NULL && pktEntry->Info.ServiceCount > 1) {
pIpInfo = DfsLookupSiteByIpaddress(pIpAddress, TRUE);
}
}
PktAcquireShared( pkt, TRUE );
//
// Look up the name the client wants a referral for
//
if ((pktEntry = PktLookupEntryByPrefix( pkt, &prefix, &remPath)) != NULL) {
//
// See if there is a better match with an 8.3 prefix
//
shortPfxEntry = PktLookupEntryByShortPrefix( pkt, &prefix, &remPath );
if ((maxLevel == 2 || maxLevel == 3) &&
shortPfxEntry != NULL &&
(shortPfxEntry->Id.Prefix.Length >
pktEntry->Id.Prefix.Length)) {
pktEntry = shortPfxEntry;
RemoveFirstComponent(&pktEntry->Id.ShortPrefix, &PrefixTail);
} else {
RemoveFirstComponent(&pktEntry->Id.Prefix, &PrefixTail);
}
ref->PathConsumed = sizeof(UNICODE_PATH_SEP) +
DomainName.Length +
PrefixTail.Length;
//
// Found an entry for the requested path. First, size the referral,
// then, if it will fit in the output buffer, marshal it in.
//
switch (maxLevel) {
case 1:
size = DfspGetV1ReferralSize(pktEntry, &DomainName); break;
case 2:
size = DfspGetV2ReferralSize(pktEntry, &DomainName); break;
case 3:
size = DfspGetV3ReferralSize(pktEntry, &DomainName); break;
default:
ASSERT(FALSE && "Invalid MaxLevel");
}
status = STATUS_SUCCESS;
//
// For level 1 referral, we fail if buffer is not big enough to
// fit entire referral. For level 2 and 3, we try to fit as many
// entries as possible into the refferal.
switch (maxLevel) {
case 1:
if (size <= OutputBufferLength) {
DfspGetV1Referral(pktEntry, &DomainName, ref);
} else {
status = STATUS_BUFFER_OVERFLOW;
DFS_TRACE_HIGH(ERROR, DfsFsctrlGetReferals_Error1, LOGSTATUS(status));
}
break;
case 2:
status = DfspGetV2Referral(pktEntry, &DomainName, OutputBufferLength, ref, &size); break;
case 3:
status = DfspGetV3Referral(pktEntry, &DomainName, pIpInfo, OutputBufferLength, ref, &size); break;
default:
ASSERT(FALSE && "Invalid MaxLevel");
}
if (status == STATUS_SUCCESS) {
*ReturnedSize = size;
}
PktRelease( pkt );
DfsReleaseIpInfo(pIpInfo);
if (DfsEventLog > 1)
LogWriteMessage(DFS_REFERRAL_REQUEST, status, 1, &prefix);
//return( status );
goto cleanup;
}
PktRelease( pkt );
}
if (pSpcInfo != NULL) {
DfsReleaseSpcInfo(pHashTable, pSpcInfo);
}
if (DfsData.IsDC == TRUE) {
//
// See if this is an FtDFS referral
//
if (maxLevel >= 3
&&
DomainName.Length > 0
&&
ShareName.Length > 0
&&
remPath.Length == 0
) {
DebugTrace( 0, Dbg, "DfsFsctrlGetReferrals: case 3\n", 0);
DebugTrace( 0, Dbg, " DomainName=%wZ\n", &DomainName);
DebugTrace( 0, Dbg, " ShareName=%wZ\n", &ShareName);
pIpInfo = DfsLookupSiteByIpaddress(pIpAddress, TRUE);
status = DfspGetV3FtDfsReferral(
&DomainName,
&ShareName,
pIpInfo,
ref,
OutputBufferLength,
ReturnedSize);
DebugTrace(-1, Dbg, "DfsFsctrlGetReferrals: exit->0x%x\n", ULongToPtr( status ));
DfsReleaseIpInfo(pIpInfo);
if (DfsEventLog > 1)
LogWriteMessage(DFS_REFERRAL_REQUEST, status, 1, &prefix);
//return( status );
goto cleanup;
}
}
//
// Nobody claimed this referral
//
DfsReleaseIpInfo(pIpInfo);
if (DfsEventLog > 1)
LogWriteMessage(DFS_REFERRAL_REQUEST, STATUS_NO_SUCH_DEVICE, 1, &prefix);
DebugTrace(-1, Dbg, "DfsFsctrlGetReferrals: exit STATUS_NO_SUCH_DEVICE\n", 0);
status = STATUS_NO_SUCH_DEVICE;
DFS_TRACE_HIGH(ERROR, DfsFsctrlGetReferals_Error2, LOGSTATUS(status));
cleanup:
DFS_TRACE_NORM(EVENT, DfsFsctrlGetReferrals_End,
LOGSTATUS(status)
LOGUSTR(arg->DfsPathName));
return(status);
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV1ReferralSize, private
//
// Synopsis: Sizes a V1 referral given a PktEntry
//
// Arguments: [PktEntry] -- The pkt entry to be returned in the referral
//
// Returns: Size in bytes of a buffer required to hold the V1 referral
// for this Pkt Entry
//
//-----------------------------------------------------------------------------
ULONG
DfspGetV1ReferralSize(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName)
{
ULONG i, size;
size = sizeof( RESP_GET_DFS_REFERRAL );
for (i = 0; i < PktEntry->Info.ServiceCount; i++) {
size += sizeof(DFS_REFERRAL_V1) +
PktEntry->Info.ServiceList[i].Address.Length +
sizeof(UNICODE_NULL);
}
return( size );
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV2ReferralSize, private
//
// Synopsis: Sizes a V2 referral given a PktEntry
//
// Arguments: [PktEntry] -- The pkt entry to be returned in the referral
//
// Returns: Size in bytes of a buffer required to hold the V2 referral
// for this Pkt Entry
//
//-----------------------------------------------------------------------------
ULONG
DfspGetV2ReferralSize(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName)
{
UNICODE_STRING PrefixTail;
ULONG i, size;
DebugTrace(+1, Dbg, "DfsPGetV2ReferralSize()\n", 0);
size = sizeof( RESP_GET_DFS_REFERRAL );
for (i = 0; i < PktEntry->Info.ServiceCount; i++) {
size += sizeof(DFS_REFERRAL_V2) +
PktEntry->Info.ServiceList[i].Address.Length +
sizeof(UNICODE_NULL);
}
RemoveFirstComponent(&PktEntry->Id.Prefix, &PrefixTail);
size += sizeof(UNICODE_PATH_SEP) +
MachineName->Length +
PrefixTail.Length +
sizeof(UNICODE_NULL);
RemoveFirstComponent(&PktEntry->Id.ShortPrefix, &PrefixTail);
size += sizeof(UNICODE_PATH_SEP) +
MachineName->Length +
PrefixTail.Length +
sizeof(UNICODE_NULL);
DebugTrace(-1, Dbg, "DfspGetV2ReferralSize() -- returning 0x%x\n", ULongToPtr( size ));
return( size );
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV3ReferralSize, private
//
// Synopsis: Sizes a V3 referral given a PktEntry
//
// Arguments: [PktEntry] -- The pkt entry to be returned in the referral
//
// Returns: Size in bytes of a buffer required to hold the V3 referral
// for this Pkt Entry
//
//-----------------------------------------------------------------------------
ULONG
DfspGetV3ReferralSize(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName)
{
UNICODE_STRING PrefixTail;
ULONG i, size;
DebugTrace(+1, Dbg, "DfsPGetV3ReferralSize()\n", 0);
size = sizeof( RESP_GET_DFS_REFERRAL );
for (i = 0; i < PktEntry->Info.ServiceCount; i++) {
size += sizeof(DFS_REFERRAL_V3) +
PktEntry->Info.ServiceList[i].Address.Length +
sizeof(UNICODE_NULL);
}
RemoveFirstComponent(&PktEntry->Id.Prefix, &PrefixTail);
size += sizeof(UNICODE_PATH_SEP) +
MachineName->Length +
PrefixTail.Length +
sizeof(UNICODE_NULL);
RemoveFirstComponent(&PktEntry->Id.ShortPrefix, &PrefixTail);
size += sizeof(UNICODE_PATH_SEP) +
MachineName->Length +
PrefixTail.Length +
sizeof(UNICODE_NULL);
DebugTrace(-1, Dbg, "DfspGetV3ReferralSize() -- returning 0x%x\n", ULongToPtr( size ));
return( size );
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV1Referral, private
//
// Synopsis: Marshals a pkt entry into a RESP_GET_DFS_REFERRAL buffer with
// V1 referrals
//
// Arguments:
//
// Returns:
//
//-----------------------------------------------------------------------------
VOID
DfspGetV1Referral(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName,
OUT PRESP_GET_DFS_REFERRAL Ref)
{
PDFS_REFERRAL_V1 pv1;
ULONG i;
Ref->NumberOfReferrals = (USHORT) PktEntry->Info.ServiceCount;
Ref->ReferralServers =
(PktEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC ? 1 : 0);
Ref->StorageServers = 1;
for (i = 0, pv1 = &Ref->Referrals[0].v1;
i < PktEntry->Info.ServiceCount;
i++) {
PDFS_SERVICE pSvc;
pSvc = &PktEntry->Info.ServiceList[i];
pv1->VersionNumber = 1;
pv1->Size = sizeof(DFS_REFERRAL_V1) +
pSvc->Address.Length +
sizeof(UNICODE_NULL);
pv1->ServerType =
pSvc->Type & DFS_SERVICE_TYPE_DOWN_LEVEL ? 0 : 1;
RtlCopyMemory(
pv1->ShareName,
pSvc->Address.Buffer,
pSvc->Address.Length);
pv1->ShareName[ pSvc->Address.Length / sizeof(WCHAR) ] =
UNICODE_NULL;
pv1 = (PDFS_REFERRAL_V1) ( ((PCHAR) pv1) + pv1->Size );
}
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV2Referral, private
//
// Synopsis: Marshals a pkt entry into a RESP_GET_DFS_REFERRAL buffer with
// V2 referrals
//
// Arguments:
//
// Returns:
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspGetV2Referral(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName,
IN ULONG BufferSize,
OUT PRESP_GET_DFS_REFERRAL Ref,
OUT PULONG ReferralSize)
{
PDFS_REFERRAL_V2 pv2;
ULONG i;
ULONG j;
LPWSTR dfsPath, alternatePath, nextAddress;
UNICODE_STRING PrefixTail;
ULONG CumulativeSize, CurrentSize;
ULONG optimalReferralCount;
USHORT totalReferrals = 0;
NTSTATUS status;
// Setup the maximum number of referrals that we intend to return.
if (DfsData.Pkt.MaxReferrals != 0) {
optimalReferralCount = DfsData.Pkt.MaxReferrals;
}
else {
optimalReferralCount = MAXIMUM_DFS_REFERRALS;
}
DebugTrace(+1, Dbg, "DfspGetV2Referral()\n", 0);
// Calculate the size of the referral, and make sure our size does not
// exceed the passed in buffer len.
CumulativeSize = sizeof (RESP_GET_DFS_REFERRAL) +
MachineName->Length + PktEntry->Id.Prefix.Length +
sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL) +
MachineName->Length + PktEntry->Id.ShortPrefix.Length +
sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL);
if (BufferSize < CumulativeSize) {
status = STATUS_BUFFER_OVERFLOW;
DFS_TRACE_HIGH(ERROR, DfspGetV2Referral_Error1,
LOGSTATUS(status)
LOGUSTR(*MachineName));
return status;
}
Ref->NumberOfReferrals = (USHORT) PktEntry->Info.ServiceCount;
Ref->ReferralServers =
(PktEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC ? 1 : 0);
Ref->StorageServers =
(PktEntry->Type & PKT_ENTRY_TYPE_OUTSIDE_MY_DOM ? 0 : 1);
pv2 = &Ref->Referrals[0].v2;
for (i = 0; i < Ref->NumberOfReferrals; i++) {
PDFS_SERVICE pSvc;
pSvc = &PktEntry->Info.ServiceList[i];
if ((pSvc->Type & DFS_SERVICE_TYPE_OFFLINE) == 0) {
totalReferrals++;
}
}
for (i = j = 0; i < Ref->NumberOfReferrals; i++) {
PDFS_SERVICE pSvc;
pSvc = &PktEntry->Info.ServiceList[i];
if ((pSvc->Type & DFS_SERVICE_TYPE_OFFLINE) == 0) {
CurrentSize = sizeof(DFS_REFERRAL_V2) + pSvc->Address.Length + sizeof(UNICODE_NULL);
if (((CumulativeSize + CurrentSize) >= BufferSize) || (j >= optimalReferralCount))
break;
j++;
CumulativeSize += CurrentSize;
}
}
// Adjust the number of referrals accordingly.
Ref->NumberOfReferrals = (USHORT)i;
//
// Copy the volume prefix into the response buffer, just past the end
// of all the V2 referrals
//
nextAddress = dfsPath = (LPWSTR) &pv2[j];
*nextAddress++ = UNICODE_PATH_SEP;
RtlCopyMemory(
nextAddress,
MachineName->Buffer,
MachineName->Length);
nextAddress += MachineName->Length/sizeof(WCHAR);
RemoveFirstComponent(&PktEntry->Id.Prefix, &PrefixTail);
RtlCopyMemory(
nextAddress,
PrefixTail.Buffer,
PrefixTail.Length);
nextAddress += PrefixTail.Length/sizeof(WCHAR);
*nextAddress++ = UNICODE_NULL;
//
// Copy the 8.3 volume prefix into the response buffer after the
// dfsPath
//
alternatePath = nextAddress;
*nextAddress++ = UNICODE_PATH_SEP;
RtlCopyMemory(
nextAddress,
MachineName->Buffer,
MachineName->Length);
nextAddress += MachineName->Length/sizeof(WCHAR);
RemoveFirstComponent(&PktEntry->Id.ShortPrefix, &PrefixTail);
RtlCopyMemory(
nextAddress,
PrefixTail.Buffer,
PrefixTail.Length);
nextAddress += PrefixTail.Length/sizeof(WCHAR);
*nextAddress++ = UNICODE_NULL;
//
// nextAddress is pointer into buffer where the individual service addresses
// will go.
//
for (i = j = 0; i < Ref->NumberOfReferrals; i++) {
PDFS_SERVICE pSvc;
//
// Only take online services
//
pSvc = &PktEntry->Info.ServiceList[i];
if ((pSvc->Type & DFS_SERVICE_TYPE_OFFLINE) == 0) {
pv2->VersionNumber = 2;
pv2->Size = sizeof(DFS_REFERRAL_V2);
pv2->ServerType =
pSvc->Type & DFS_SERVICE_TYPE_DOWN_LEVEL ? 0 : 1;
pv2->Proximity = 0;
pv2->TimeToLive = PktEntry->Info.Timeout;
pv2->DfsPathOffset = (USHORT) (((PCHAR) dfsPath) - ((PCHAR) pv2));
pv2->DfsAlternatePathOffset =
(USHORT) (((PCHAR) alternatePath) - ((PCHAR) pv2));
pv2->NetworkAddressOffset =
(USHORT) (((PCHAR) nextAddress) - ((PCHAR) pv2));
RtlCopyMemory(
nextAddress,
pSvc->Address.Buffer,
pSvc->Address.Length);
nextAddress[ pSvc->Address.Length/sizeof(WCHAR) ] = UNICODE_NULL;
nextAddress += pSvc->Address.Length/sizeof(WCHAR) + 1;
pv2++;
j++;
}
}
Ref->NumberOfReferrals = (USHORT) j;
//
// we have more than one service, but cannot fit any into the buffer
// return buffer overflow.
//
if ((totalReferrals > 0) && (Ref->NumberOfReferrals == 0)) {
status = STATUS_BUFFER_OVERFLOW;
DFS_TRACE_HIGH(ERROR, DfspGetV2Referral_Error2,
LOGSTATUS(status)
LOGUSTR(*MachineName));
return status;
}
*ReferralSize = (ULONG)((PUCHAR)nextAddress - (PUCHAR)Ref);
DebugTrace(-1, Dbg, "DfspGetV2Referral() -- exit\n", 0);
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV3Referral, private
//
// Synopsis: Marshals a pkt entry into a RESP_GET_DFS_REFERRAL buffer with
// V3 referrals
//
// Arguments:
//
// Returns:
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspGetV3Referral(
IN PDFS_PKT_ENTRY PktEntry,
IN PUNICODE_STRING MachineName,
IN PDFS_IP_INFO pIpInfo,
IN ULONG BufferSize,
OUT PRESP_GET_DFS_REFERRAL Ref,
OUT PULONG ReferralSize)
{
PDFS_REFERRAL_V3 pv3;
ULONG i;
ULONG j;
LPWSTR dfsPath, alternatePath, nextAddress;
UNICODE_STRING PrefixTail;
PDFS_REFERRAL_LIST pRefList = NULL;
ULONG CumulativeSize, Ndx, CurrentSize;
USHORT totalReferrals;
ULONG InSite;
ULONG optimalReferralCount;
NTSTATUS status;
// Setup the maximum number of referrals that we intend to return.
if (DfsData.Pkt.MaxReferrals != 0) {
optimalReferralCount = DfsData.Pkt.MaxReferrals;
}
else {
optimalReferralCount = MAXIMUM_DFS_REFERRALS;
}
DebugTrace(+1, Dbg, "DfspGetV3Referral()\n", 0);
// Calculate the size of the referral, and make sure our size does not
// exceed the passed in buffer len.
CumulativeSize =
sizeof (RESP_GET_DFS_REFERRAL) +
MachineName->Length + PktEntry->Id.Prefix.Length +
sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL) +
MachineName->Length + PktEntry->Id.ShortPrefix.Length +
sizeof(UNICODE_PATH_SEP) + sizeof(UNICODE_NULL);
if (BufferSize < CumulativeSize) {
status = STATUS_BUFFER_OVERFLOW;
DFS_TRACE_HIGH(ERROR, DfspGetV3Referral_Error1,
LOGSTATUS(status)
LOGUSTR(*MachineName));
return status;
}
Ref->NumberOfReferrals = (USHORT) PktEntry->Info.ServiceCount;
totalReferrals = Ref->NumberOfReferrals;
//
// Alloc and init a DFS_REFERRAL_LIST
//
if (Ref->NumberOfReferrals) {
pRefList = ExAllocatePoolWithTag(
PagedPool,
sizeof(DFS_REFERRAL_LIST) * Ref->NumberOfReferrals,
' sfD');
if (pRefList == NULL) {
DebugTrace(-1,
Dbg,
"DfspGetV3Referral() exit STATUS_INSUFFICIENT_RESOURCES\n",
0);
status = STATUS_INSUFFICIENT_RESOURCES;
DFS_TRACE_HIGH(ERROR, DfspGetV3Referral_Error2,
LOGSTATUS(status)
LOGUSTR(*MachineName));
return status;
}
}
//
// Initialize it
//
for (i = j = 0; i < Ref->NumberOfReferrals; i++) {
//
// Skip offline entries
//
if ((PktEntry->Info.ServiceList[i].Type & DFS_SERVICE_TYPE_OFFLINE) == 0) {
pRefList[j].pName = PktEntry->Info.ServiceList[i].Name;
pRefList[j].pAddress = PktEntry->Info.ServiceList[i].Address;
pRefList[j].Type = PktEntry->Info.ServiceList[i].Type;
j++;
}
}
Ref->NumberOfReferrals = (USHORT)j;
if (Ref->NumberOfReferrals) {
//
// Shuffle the list in case we don't have site info
//
SrvShuffle(pRefList, 0, Ref->NumberOfReferrals - 1);
//
// Determine client's site based on the IP address srv gave us.
//
if (pIpInfo != NULL) {
//
// Reorder by site
//
InSite = DfsIpOrdering(pIpInfo, Ref->NumberOfReferrals, pRefList);
if (PktEntry->Type & PKT_ENTRY_TYPE_INSITE_ONLY) {
Ref->NumberOfReferrals = (USHORT)InSite;
}
}
}
Ref->ReferralServers =
(PktEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC ? 1 : 0);
Ref->StorageServers =
(PktEntry->Type & PKT_ENTRY_TYPE_OUTSIDE_MY_DOM ? 0 : 1);
totalReferrals = Ref->NumberOfReferrals;
pv3 = &Ref->Referrals[0].v3;
//
// Now, we have a shuffled list of referrals. see how many we can give back
// to the client in the buffer allocated.
// We pick the first entry, followed by the last entry, then the second
// entry followed by the last but one, and so on. The purpose of this
// logic is to ensure that we split the returned referrals between
// the "insite" referrals that are on the top of the list and the
// "outsite" referrals that are at the bottom
//
for (i = 0; i < Ref->NumberOfReferrals; i++) {
Ndx = i / 2;
if (i & 1) {
Ndx = Ref->NumberOfReferrals - Ndx - 1;
}
CurrentSize = sizeof(DFS_REFERRAL_V3) +
pRefList[Ndx].pAddress.Length + sizeof(UNICODE_NULL);
if (((CumulativeSize + CurrentSize) >= BufferSize) || (i >= optimalReferralCount))
break;
CumulativeSize += CurrentSize;
}
// Adjust the number of referrals accordingly.
Ref->NumberOfReferrals = (USHORT)i;
//
// Copy the volume prefix into the response buffer, just past the end
// of all the V3 referrals
//
nextAddress = dfsPath = (LPWSTR) &pv3[ Ref->NumberOfReferrals ];
*nextAddress++ = UNICODE_PATH_SEP;
RtlCopyMemory(
nextAddress,
MachineName->Buffer,
MachineName->Length);
nextAddress += MachineName->Length/sizeof(WCHAR);
RemoveFirstComponent(&PktEntry->Id.Prefix, &PrefixTail);
RtlCopyMemory(
nextAddress,
PrefixTail.Buffer,
PrefixTail.Length);
nextAddress += PrefixTail.Length/sizeof(WCHAR);
*nextAddress++ = UNICODE_NULL;
//
// Copy the 8.3 volume prefix into the response buffer after the
// dfsPath
//
alternatePath = nextAddress;
*nextAddress++ = UNICODE_PATH_SEP;
RtlCopyMemory(
nextAddress,
MachineName->Buffer,
MachineName->Length);
nextAddress += MachineName->Length/sizeof(WCHAR);
RemoveFirstComponent(&PktEntry->Id.ShortPrefix, &PrefixTail);
RtlCopyMemory(
nextAddress,
PrefixTail.Buffer,
PrefixTail.Length);
nextAddress += PrefixTail.Length/sizeof(WCHAR);
*nextAddress++ = UNICODE_NULL;
//
// nextAddress is pointer into buffer where the individual service addresses
// will go.
//
for (i = 0; i < Ref->NumberOfReferrals; i++) {
if (i < ((ULONG)Ref->NumberOfReferrals + 1) / 2) {
Ndx = i;
}
else {
Ndx = totalReferrals - Ref->NumberOfReferrals + i;
}
pv3->VersionNumber = 3;
pv3->Size = sizeof(DFS_REFERRAL_V3);
pv3->ServerType = pRefList[Ndx].Type & DFS_SERVICE_TYPE_DOWN_LEVEL ? 0 : 1;
pv3->StripPath = 0; // for now
pv3->NameListReferral = 0;
pv3->TimeToLive = PktEntry->Info.Timeout;
pv3->DfsPathOffset = (USHORT) (((PCHAR) dfsPath) - ((PCHAR) pv3));
pv3->DfsAlternatePathOffset =
(USHORT) (((PCHAR) alternatePath) - ((PCHAR) pv3));
pv3->NetworkAddressOffset =
(USHORT) (((PCHAR) nextAddress) - ((PCHAR) pv3));
RtlZeroMemory(
&pv3->ServiceSiteGuid,
sizeof (GUID));
RtlCopyMemory(
nextAddress,
pRefList[Ndx].pAddress.Buffer,
pRefList[Ndx].pAddress.Length);
nextAddress[ pRefList[Ndx].pAddress.Length/sizeof(WCHAR) ] = UNICODE_NULL;
nextAddress += pRefList[Ndx].pAddress.Length/sizeof(WCHAR) + 1;
pv3++;
}
if (pRefList) {
ExFreePool(pRefList);
}
//
// we have more than one service, but cannot fit any into the buffer
// return buffer overflow.
//
if ((totalReferrals > 0) && (Ref->NumberOfReferrals == 0)) {
return STATUS_BUFFER_OVERFLOW;
}
*ReferralSize = (ULONG)((PUCHAR)nextAddress - (PUCHAR)Ref);
DebugTrace(-1, Dbg, "DfspGetV3Referral() -- returning STATUS_SUCCESS\n", 0);
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetOneV3SpecialReferral, private
//
// Synopsis: Marshals a special referral
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspGetOneV3SpecialReferral(
IN PDFS_SPECIAL_INFO pSpcInfo,
IN PUNICODE_STRING Name,
IN PDFS_IP_INFO pIpInfo,
OUT PRESP_GET_DFS_REFERRAL Ref,
IN ULONG MaximumSize,
OUT PULONG ReturnedSize)
{
PDFS_REFERRAL_V3 pv3;
PDFS_REFERRAL_LIST pRefList;
LPWSTR pwName;
LONG i, j;
ULONG size;
NTSTATUS status;
DFS_REFERRAL_LIST MustKeep;
BOOLEAN FoundMustKeep;
ULONG CumulativeSize;
ULONG CurrentSize;
ULONG NameCount;
// ASSERT(Name->Length > 0);
DebugTrace(+1, Dbg, "DfspGetOneV3SpecialReferral(%ws)\n", Name->Buffer);
//
// Calculate the size
//
if (pSpcInfo == NULL) {
status = STATUS_NO_SUCH_DEVICE;
DebugTrace(-1, Dbg, "DfspGetOneV3SpecialReferral() -- exit STATUS_NO_SUCH_DEVICE\n", 0);
DFS_TRACE_HIGH(ERROR, DfspGetOneV3SpecialReferral_Error1,
LOGSTATUS(status)
LOGUSTR(*Name));
return status;
}
CumulativeSize = sizeof( RESP_GET_DFS_REFERRAL ) +
sizeof(DFS_REFERRAL_V3) +
pSpcInfo->SpecialName.Length +
sizeof(UNICODE_NULL) +
sizeof(UNICODE_NULL);
if (MaximumSize < CumulativeSize) {
*((PULONG) Ref) = CumulativeSize + sizeof(ULONG);
return STATUS_BUFFER_OVERFLOW;
}
//
// Alloc and init a DFS_REFERRAL_LIST
//
pRefList = ExAllocatePoolWithTag(
PagedPool,
sizeof(DFS_REFERRAL_LIST) * pSpcInfo->NameCount,
' sfD');
if (pRefList == NULL) {
DebugTrace(-1,
Dbg,
"DfspGetOneV3SpecialReferral() exit STATUS_INSUFFICIENT_RESOURCES\n",
0);
status = STATUS_INSUFFICIENT_RESOURCES;
DFS_TRACE_HIGH(ERROR, DfspGetOneV3SpecialRefferal_Error2,
LOGSTATUS(status)
LOGUSTR(*Name));
return status;
}
//
// Initialize it
//
for (j = 0; j < pSpcInfo->NameCount; j++) {
pRefList[j].pName = pSpcInfo->Name[j];
pRefList[j].pAddress = pSpcInfo->Name[j];
pRefList[j].Type = 0;
}
//
// Shuffle the list in case we don't have site info
//
SrvShuffle(pRefList, 1, pSpcInfo->NameCount - 1);
NameCount = 0;
if (pSpcInfo->NameCount > 0) {
MustKeep = pRefList[0];
CurrentSize = sizeof(UNICODE_PATH_SEP);
CurrentSize += MustKeep.pName.Length;
CurrentSize += sizeof(UNICODE_NULL);
CumulativeSize += CurrentSize;
}
//
// Determine client's site based on the IP address srv gave us.
//
if (pIpInfo != NULL) {
//
// Reorder by site
//
DfsIpOrdering(pIpInfo, pSpcInfo->NameCount, pRefList);
}
FoundMustKeep = FALSE;
for (j = 0; j < pSpcInfo->NameCount; j++) {
if ((FoundMustKeep == FALSE) &&
(MustKeep.pName.Buffer == pRefList[j].pName.Buffer)) {
FoundMustKeep = TRUE;
if (CumulativeSize >= MaximumSize) {
break;
}
}
else {
CurrentSize = sizeof(UNICODE_PATH_SEP);
CurrentSize += pRefList[j].pName.Length;
CurrentSize += sizeof(UNICODE_NULL);
if (CumulativeSize + CurrentSize >= MaximumSize) {
break;
}
CumulativeSize += CurrentSize;
}
}
if ((pSpcInfo->NameCount > 0) && (FoundMustKeep == FALSE)) {
if (CumulativeSize < MaximumSize) {
NameCount++;
pRefList[j] = MustKeep;
}
}
NameCount += j;
if ((NameCount == 0) && (pSpcInfo->NameCount > 0)) {
*ReturnedSize = CumulativeSize + CurrentSize;
ExFreePool(pRefList);
return STATUS_BUFFER_OVERFLOW;
}
//
// Fill in the referral
//
DebugTrace( 0, Dbg, "DfspGetOneV3SpecialReferral pSpcInfo = 0x%x\n", pSpcInfo);
Ref->NumberOfReferrals = 1;
Ref->StorageServers = Ref->ReferralServers = 0;
Ref->PathConsumed = 0;
pv3 = &Ref->Referrals[0].v3;
pv3->NumberOfExpandedNames = (USHORT) NameCount;
//
// Copy the Special Names right after the V3 referral
//
pwName = (LPWSTR) &pv3[1];
pv3->SpecialNameOffset = (USHORT) (((PCHAR) pwName) - ((PCHAR) pv3));
pv3->VersionNumber = 3;
pv3->Size = sizeof(DFS_REFERRAL_V3);
pv3->ServerType = 0;
pv3->StripPath = 0; // for now
pv3->NameListReferral = 1;
pv3->TimeToLive = DfsData.SpcHashTable->SpcTimeout;
*pwName++ = UNICODE_PATH_SEP;
RtlCopyMemory(
pwName,
pSpcInfo->SpecialName.Buffer,
pSpcInfo->SpecialName.Length);
pwName[ pSpcInfo->SpecialName.Length/sizeof(WCHAR) ] = UNICODE_NULL;
pwName += pSpcInfo->SpecialName.Length/sizeof(WCHAR) + 1;
pv3->ExpandedNameOffset = (USHORT) (((PCHAR) pwName) - ((PCHAR) pv3));
for (j = 0; j < pv3->NumberOfExpandedNames; j++) {
*pwName++ = UNICODE_PATH_SEP;
RtlCopyMemory(
pwName,
pRefList[j].pName.Buffer,
pRefList[j].pName.Length);
pwName[ pRefList[j].pName.Length/sizeof(WCHAR) ] = UNICODE_NULL;
pwName += pRefList[j].pName.Length/sizeof(WCHAR) + 1;
}
// Double UNICODE_NULL at end
*pwName++ = UNICODE_NULL;
*ReturnedSize = CumulativeSize;
ExFreePool(pRefList);
DebugTrace(-1, Dbg, "DfspGetOneV3SpecialReferral() -- exit\n", 0);
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetAllV3SpecialReferral, private
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspGetAllV3SpecialReferral(
IN PDFS_IP_INFO pIpInfo,
OUT PRESP_GET_DFS_REFERRAL Ref,
IN ULONG MaximumSize,
OUT PULONG ReturnedSize)
{
PDFS_REFERRAL_V3 pv3;
PDFS_SPECIAL_INFO pSpcInfo;
PSPECIAL_HASH_TABLE pHashTable = DfsData.SpcHashTable;
PDFS_REFERRAL_LIST pRefList;
LPWSTR pwName;
LONG i, j;
ULONG size;
ULONG SpcCount;
NTSTATUS status;
DebugTrace(+1, Dbg, "DfspGetAllV3SpecialReferral()\n", 0);
//
// return all special names, with DFS_SPECIAL_INFO_PRIMARY's expanded
//
size = sizeof( RESP_GET_DFS_REFERRAL );
DfsSpcInfoFindOpen(pHashTable);
for (SpcCount = 0, pSpcInfo = DfsSpcInfoFindFirst(pHashTable);
pSpcInfo != NULL;
pSpcInfo = DfsSpcInfoFindNext(pHashTable,pSpcInfo)) {
//
// Skip downlevel trusts - they can't have a SYSVOL or an FtDfs
// in them.
//
if (pSpcInfo->TrustType != TRUST_TYPE_UPLEVEL)
continue;
size += sizeof(DFS_REFERRAL_V3) +
sizeof(UNICODE_PATH_SEP) +
pSpcInfo->SpecialName.Length +
sizeof(UNICODE_NULL);
if ((pSpcInfo->TypeFlags & DFS_SPECIAL_INFO_PRIMARY) != 0) {
for (j = 0; j < pSpcInfo->NameCount; j++) {
size += sizeof(UNICODE_PATH_SEP) +
pSpcInfo->Name[j].Length +
sizeof(UNICODE_NULL);
}
}
//
// Double UNICODE_NULL at end
//
size += sizeof(WCHAR);
SpcCount++;
}
//
// If no entries to return say so
//
if (SpcCount == 0) {
DfsSpcInfoFindClose(pHashTable);
if (pIpInfo != NULL) {
DfsReleaseIpInfo(pIpInfo);
}
DebugTrace(-1, Dbg, "DfspGetAllV3SpecialReferral() -- exit STATUS_NO_SUCH_DEVICE\n", 0);
status = STATUS_NO_SUCH_DEVICE;
DFS_TRACE_HIGH(ERROR, DfspGetAllV3SpecialReferral_Error1, LOGSTATUS(status));
return status;
}
//
// See if it will fit - respond with needed size if it doesn't
//
if (size > MaximumSize) {
*((PULONG) Ref) = size;
*ReturnedSize = sizeof(ULONG);
DfsSpcInfoFindClose(pHashTable);
if (pIpInfo != NULL) {
DfsReleaseIpInfo(pIpInfo);
}
DebugTrace(-1, Dbg, "DfspGetAllV3SpecialReferral() -- exit STATUS_BUFFER_OVERFLOW\n", 0);
status = STATUS_BUFFER_OVERFLOW;
DFS_TRACE_HIGH(ERROR, DfspGetAllV3SpecialReferral_Error2, LOGSTATUS(status));
return STATUS_BUFFER_OVERFLOW;
}
*ReturnedSize = size;
Ref->NumberOfReferrals = (USHORT) SpcCount;
Ref->StorageServers = Ref->ReferralServers = 0;
Ref->PathConsumed = 0;
pv3 = &Ref->Referrals[0].v3;
//
// Copy the Special Names right after the V3 referrals
//
pwName = (LPWSTR) &pv3[SpcCount];
for (i = 0, pSpcInfo = DfsSpcInfoFindFirst(pHashTable);
pSpcInfo != NULL;
pSpcInfo = DfsSpcInfoFindNext(pHashTable,pSpcInfo)) {
//
// Skip downlevel trusts - they can't have a SYSVOL or an FtDfs
// in them.
//
if (pSpcInfo->TrustType != TRUST_TYPE_UPLEVEL)
continue;
pv3[i].SpecialNameOffset = (USHORT) (((PCHAR) pwName) - ((PCHAR) &pv3[i]));
pv3[i].VersionNumber = 3;
pv3[i].Size = sizeof(DFS_REFERRAL_V3);
pv3[i].ServerType = 0;
pv3[i].StripPath = 0; // for now
pv3[i].NameListReferral = 1;
pv3[i].TimeToLive = pHashTable->SpcTimeout;
*pwName++ = UNICODE_PATH_SEP;
RtlCopyMemory(
pwName,
pSpcInfo->SpecialName.Buffer,
pSpcInfo->SpecialName.Length);
pwName[ pSpcInfo->SpecialName.Length/sizeof(WCHAR) ] = UNICODE_NULL;
pwName += pSpcInfo->SpecialName.Length/sizeof(WCHAR) + 1;
if ((pSpcInfo->TypeFlags & DFS_SPECIAL_INFO_PRIMARY) != 0) {
//
// Alloc and init a DFS_REFERRAL_LIST
//
pRefList = ExAllocatePoolWithTag(
PagedPool,
sizeof(DFS_REFERRAL_LIST) * pSpcInfo->NameCount,
' sfD');
if (pRefList == NULL) {
DfsSpcInfoFindClose(pHashTable);
if (pIpInfo != NULL) {
DfsReleaseIpInfo(pIpInfo);
}
DebugTrace(-1,
Dbg,
"DfspGetAllV3SpecialReferral() exit STATUS_INSUFFICIENT_RESOURCES\n",
0);
status = STATUS_INSUFFICIENT_RESOURCES;
DFS_TRACE_HIGH(ERROR, DfspGetAllV3SpecialReferral_Error3, LOGSTATUS(status));
return status;
}
//
// Initialize it
//
for (j = 0; j < pSpcInfo->NameCount; j++) {
pRefList[j].pName = pSpcInfo->Name[j];
pRefList[j].pAddress = pSpcInfo->Name[j];
pRefList[j].Type = 0;
}
//
// Shuffle the list in case we don't have site info
//
SrvShuffle(pRefList, 0, pSpcInfo->NameCount - 1);
//
// Reorder by site
//
//
// Determine client's site based on the IP address srv gave us.
//
if (pSpcInfo->NameCount > 1 && pIpInfo != NULL) {
//
// Reorder by site
//
DfsIpOrdering(pIpInfo, pSpcInfo->NameCount, pRefList);
}
//
// Load the referrals
//
pv3[i].NumberOfExpandedNames = (USHORT) pSpcInfo->NameCount;
pv3[i].ExpandedNameOffset = (USHORT) (((PCHAR) pwName) - ((PCHAR) &pv3[i]));
for (j = 0; j < pSpcInfo->NameCount; j++) {
*pwName++ = UNICODE_PATH_SEP;
RtlCopyMemory(
pwName,
pRefList[j].pName.Buffer,
pRefList[j].pName.Length);
pwName[ pRefList[j].pName.Length/sizeof(WCHAR) ] = UNICODE_NULL;
pwName += pRefList[j].pName.Length/sizeof(WCHAR) + 1;
}
ExFreePool(pRefList);
// Double UNICODE_NULL at end
*pwName++ = UNICODE_NULL;
} else {
pv3[i].NumberOfExpandedNames = 0;
pv3[i].ExpandedNameOffset = 0;
}
i++;
}
DfsSpcInfoFindClose(pHashTable);
DebugTrace(-1, Dbg, "DfspGetAllV3SpecialReferral() -- exit\n", 0);
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------------
//
// Function: DfspGetV3FtDfsReferral, private
//
//-----------------------------------------------------------------------------
NTSTATUS
DfspGetV3FtDfsReferral(
IN PUNICODE_STRING DomainName,
IN PUNICODE_STRING ShareName,
IN PDFS_IP_INFO pIpInfo,
OUT PRESP_GET_DFS_REFERRAL Ref,
IN ULONG MaximumSize,
PULONG ReturnedSize)
{
NTSTATUS status;
PDFS_REFERRAL_V3 pv3;
PSPECIAL_HASH_TABLE pFtHashTable = DfsData.FtDfsHashTable;
PDFS_REFERRAL_LIST pRefList;
PDFS_SPECIAL_INFO pFtInfo = NULL;
LPWSTR dfsPath, alternatePath, ustrp;
LONG i, j;
ULONG k;
ULONG size;
UNICODE_STRING MachineName;
UNICODE_STRING TempName;
LARGE_INTEGER now;
PDFS_SPECIAL_INFO pSpcInfo = NULL;
PSPECIAL_HASH_TABLE pHashTable = DfsData.SpcHashTable;
BOOLEAN fSysvol = FALSE;
DFS_REFERRAL_LIST MustKeep;
BOOLEAN SpecialInfoCreated = FALSE;
BOOLEAN FoundMustKeep;
ULONG CumulativeSize;
ULONG CurrentSize;
ULONG TotalCount;
DebugTrace(+1, Dbg, "DfspGetV3FtDfsReferral()\n", 0);
if (DfspIsSpecialShare(ShareName) == TRUE) {
pSpcInfo = DfspLookupSpcEntry(DomainName);
if (pSpcInfo == NULL || pSpcInfo->NameCount == 0) {
status = STATUS_NO_SUCH_DEVICE;
goto ErrorOnSpecialShare;
}
size = sizeof(DFS_SPECIAL_INFO) + DomainName->Length;
if (pSpcInfo->NameCount > 1)
size += sizeof(UNICODE_STRING) * (pSpcInfo->NameCount - 1);
for (i = 0; i < pSpcInfo->NameCount; i++) {
size += sizeof(UNICODE_PATH_SEP) +
pSpcInfo->Name[i].Length +
sizeof(UNICODE_PATH_SEP) +
ShareName->Length;
}
pFtInfo = ExAllocatePoolWithTag(
PagedPool,
size,
' sfD');
if (pFtInfo == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorOnSpecialShare;
}
RtlZeroMemory(pFtInfo, size);
ustrp = (LPWSTR) &pFtInfo->Name[pSpcInfo->NameCount];
pFtInfo->SpecialName.Length = DomainName->Length;
pFtInfo->SpecialName.MaximumLength = DomainName->MaximumLength;
pFtInfo->SpecialName.Buffer = ustrp;
RtlCopyMemory(
ustrp,
DomainName->Buffer,
DomainName->Length);
ustrp += DomainName->Length / sizeof(WCHAR);
//
// NOTE:
// By setting UseCount to 1 and SPECIAL_INFO_DELETE_PENDING, the
// call to DfsReleaseSpcInfo() will free up this chunk of memory.
//
InitializeListHead(&pFtInfo->HashChain);
pFtInfo->NodeTypeCode = DFS_NTC_SPECIAL_INFO;
pFtInfo->NodeByteSize = (USHORT) size;
pFtInfo->UseCount = 1;
pFtInfo->Flags = SPECIAL_INFO_DELETE_PENDING;
pFtInfo->NameCount = pSpcInfo->NameCount;
SpecialInfoCreated = TRUE;
for (i = 0; i < pSpcInfo->NameCount; i++) {
pFtInfo->Name[i].Length = sizeof(UNICODE_PATH_SEP) +
pSpcInfo->Name[i].Length +
sizeof(UNICODE_PATH_SEP) +
ShareName->Length;
pFtInfo->Name[i].MaximumLength = pFtInfo->Name[i].Length;
pFtInfo->Name[i].Buffer = ustrp;
*ustrp++ = UNICODE_PATH_SEP;
RtlCopyMemory(
ustrp,
pSpcInfo->Name[i].Buffer,
pSpcInfo->Name[i].Length);
ustrp += pSpcInfo->Name[i].Length / sizeof(WCHAR);
*ustrp++ = UNICODE_PATH_SEP;
RtlCopyMemory(
ustrp,
ShareName->Buffer,
ShareName->Length);
ustrp += ShareName->Length / sizeof(WCHAR);
}
if (pSpcInfo != NULL) {
DfsReleaseSpcInfo(pHashTable, pSpcInfo);
pSpcInfo = NULL;
}
fSysvol = TRUE;
goto CreateReferral;
ErrorOnSpecialShare:
if (pSpcInfo != NULL) {
DfsReleaseSpcInfo(pHashTable, pSpcInfo);
pSpcInfo = NULL;
}
DebugTrace(-1, Dbg,
"DfspGetV3FtDfsReferral(Syvol or NetLogon) -- exit STATUS_NO_SUCH_DEVICE\n", 0);
status = STATUS_NO_SUCH_DEVICE;
DFS_TRACE_HIGH(ERROR, DfspGetV3FtDfsReferral_Error1,
LOGSTATUS(status)
LOGUSTR(*DomainName)
LOGUSTR(*ShareName));
return status;
}
//
// Check the FtDfs cache
//
pFtInfo = DfsLookupSpcInfo(
pFtHashTable,
ShareName);
//
// If the entry is old try to refresh it.
//
KeQuerySystemTime(&now);
if (pFtInfo != NULL && now.QuadPart > pFtInfo->ExpireTime.QuadPart) {
ExAcquireFastMutex( &pFtHashTable->HashListMutex );
pFtInfo->ExpireTime.QuadPart = now.QuadPart + UInt32x32To64(
10 * 60,
10 * 1000 * 1000);
ExReleaseFastMutex( &pFtHashTable->HashListMutex );
DfsReleaseSpcInfo(pFtHashTable, pFtInfo);
pFtInfo = NULL;
}
if (pFtInfo == NULL) {
//
// Try to get it into the cache
//
DfsLpcDomRequest(ShareName);
//
// And try again
//
pFtInfo = DfsLookupSpcInfo(
pFtHashTable,
ShareName);
if (pFtInfo == NULL) {
//
// Not in the cache, and we couldn't get it
//
DebugTrace(-1, Dbg, "DfspGetV3FtDfsReferral(1) -- exit STATUS_NO_SUCH_DEVICE\n", 0);
status = STATUS_NO_SUCH_DEVICE;
DFS_TRACE_HIGH(ERROR, DfspGetV3FtDfsReferral_Error2,
LOGSTATUS(status)
LOGUSTR(*DomainName)
LOGUSTR(*ShareName));
return status;
}
}
if (pFtInfo->NameCount == 0) {
DfsReleaseSpcInfo(pFtHashTable, pFtInfo);
DebugTrace(-1, Dbg, "DfspGetOneV3SpecialReferral(2) -- exit STATUS_NO_SUCH_DEVICE\n", 0);
status = STATUS_NO_SUCH_DEVICE;
DFS_TRACE_HIGH(ERROR, DfspGetV3FtDfsReferral_Error3,
LOGSTATUS(status)
LOGUSTR(*DomainName)
LOGUSTR(*ShareName));
return STATUS_NO_SUCH_DEVICE;
}
CreateReferral:
CumulativeSize = sizeof( RESP_GET_DFS_REFERRAL );
// Longname
CumulativeSize += sizeof(UNICODE_PATH_SEP) +
DomainName->Length +
sizeof(UNICODE_PATH_SEP) +
ShareName->Length +
sizeof(UNICODE_NULL);
// Shortname
CumulativeSize += sizeof(UNICODE_PATH_SEP) +
DomainName->Length +
sizeof(UNICODE_PATH_SEP) +
ShareName->Length +
sizeof(UNICODE_NULL);
if (CumulativeSize > MaximumSize) {
*((PULONG) Ref) = (CumulativeSize + sizeof(ULONG));
*ReturnedSize = sizeof(ULONG);
DfsReleaseSpcInfo(pFtHashTable, pFtInfo);
return STATUS_BUFFER_OVERFLOW;
}
//
// Alloc and init a DFS_REFERRAL_LIST
//
pRefList = ExAllocatePoolWithTag(
PagedPool,
sizeof(DFS_REFERRAL_LIST) * pFtInfo->NameCount,
' sfD');
if (pRefList == NULL) {
DfsReleaseSpcInfo(pFtHashTable, pFtInfo);
DebugTrace(-1,
Dbg,
"DfspGetV3FtDfsReferral exit STATUS_INSUFFICIENT_RESOURCES\n",
0);
status = STATUS_INSUFFICIENT_RESOURCES;
DFS_TRACE_HIGH(ERROR, DfspGetV3FtDfsReferral_Error4,
LOGSTATUS(status)
LOGUSTR(*DomainName)
LOGUSTR(*ShareName));
return status;
}
//
// Initialize it
//
for (i = 0; i < pFtInfo->NameCount; i++) {
//
// We need to extract the servername from the address
//
pRefList[i].pName = pFtInfo->Name[i];
pRefList[i].pName.Buffer++;
pRefList[i].pName.Length -= sizeof(WCHAR);
for (k = 0;
k < pRefList[i].pName.Length/sizeof(WCHAR)
&&
pRefList[i].pName.Buffer[k] != L'\\';
k++) {
/* NOTHING */;
}
if (k < pRefList[i].pName.Length/sizeof(WCHAR)) {
pRefList[i].pName.Length = (USHORT) (k * sizeof(WCHAR));
}
pRefList[i].pAddress = pFtInfo->Name[i];
pRefList[i].Type = 0;
}
//
// Shuffle the list in case we don't have site info
//
SrvShuffle(pRefList, (SpecialInfoCreated == TRUE) ? 1 : 0, pFtInfo->NameCount - 1);
TotalCount = 0;
if ((SpecialInfoCreated == TRUE) && (pFtInfo->NameCount > 0)) {
MustKeep = pRefList[0];
CurrentSize = sizeof(UNICODE_PATH_SEP);
CurrentSize += MustKeep.pAddress.Length;
CurrentSize += sizeof(UNICODE_NULL);
CumulativeSize += CurrentSize;
}
//
// Determine client's site based on the IP address srv gave us.
//
if (pIpInfo != NULL) {
//
// Reorder by site
//
DfsIpOrdering(pIpInfo, pFtInfo->NameCount, pRefList);
}
FoundMustKeep = FALSE;
for (i = 0; i < pFtInfo->NameCount; i++) {
if ((SpecialInfoCreated == TRUE) &&
(FoundMustKeep == FALSE) &&
(MustKeep.pAddress.Buffer == pRefList[i].pAddress.Buffer)) {
FoundMustKeep = TRUE;
if (CumulativeSize >= MaximumSize) {
break;
}
}
else {
CurrentSize = sizeof(DFS_REFERRAL_V3) +
pRefList[i].pAddress.Length +
sizeof(UNICODE_NULL);
if ((CumulativeSize + CurrentSize) >= MaximumSize) {
break;
}
CumulativeSize += CurrentSize;
}
}
if ((pFtInfo->NameCount > 0) && (SpecialInfoCreated == TRUE) && (FoundMustKeep == FALSE)) {
if (CumulativeSize < MaximumSize) {
TotalCount++;
pRefList[i] = MustKeep;
}
}
TotalCount += i;
if ((TotalCount == 0) && (pFtInfo->NameCount > 0)) {
*ReturnedSize = CumulativeSize + CurrentSize;
DfsReleaseSpcInfo(pFtHashTable, pFtInfo);
ExFreePool(pRefList);
return STATUS_BUFFER_OVERFLOW;
}
//
// Fill in the referral
//
Ref->NumberOfReferrals = (USHORT) TotalCount;
Ref->ReferralServers = 1; // Means the FtDFS roots can handle referrals (should be true)
Ref->StorageServers = 1; // Means the FtDFS roots contain data/storage (should be true)
pv3 = &Ref->Referrals[0].v3;
//
// Copy the FtDfs prefix into the response buffer, just past the end
// of all the V3 referrals
//
dfsPath = ustrp = (LPWSTR) &pv3[ TotalCount ];
*ustrp++ = UNICODE_PATH_SEP;
RtlCopyMemory(
ustrp,
DomainName->Buffer,
DomainName->Length);
ustrp += DomainName->Length / sizeof(WCHAR);
*ustrp++ = UNICODE_PATH_SEP;
RtlCopyMemory(
ustrp,
ShareName->Buffer,
ShareName->Length);
ustrp += ShareName->Length / sizeof(WCHAR);
*ustrp++ = UNICODE_NULL;
//
// Copy the 8.3 volume prefix into the response buffer after the
// dfsPath
//
alternatePath = ustrp;
*ustrp++ = UNICODE_PATH_SEP;
RtlCopyMemory(
ustrp,
DomainName->Buffer,
DomainName->Length);
ustrp += DomainName->Length / sizeof(WCHAR);
*ustrp++ = UNICODE_PATH_SEP;
RtlCopyMemory(
ustrp,
ShareName->Buffer,
ShareName->Length);
ustrp += ShareName->Length / sizeof(WCHAR);
*ustrp++ = UNICODE_NULL;
//
// Initialize pointer into buffer where the individual service addresses
// will go.
//
for (i = 0; i < Ref->NumberOfReferrals; i++) {
pv3->VersionNumber = 3;
pv3->Size = sizeof(DFS_REFERRAL_V3);
pv3->ServerType = (fSysvol == TRUE) ? 0 : 1;
pv3->StripPath = 0; // for now
pv3->NameListReferral = 0;
pv3->TimeToLive = pFtHashTable->SpcTimeout;
pv3->DfsPathOffset = (USHORT) (((PCHAR) dfsPath) - ((PCHAR) pv3));
pv3->DfsAlternatePathOffset =
(USHORT) (((PCHAR) alternatePath) - ((PCHAR) pv3));
pv3->NetworkAddressOffset =
(USHORT) (((PCHAR) ustrp) - ((PCHAR) pv3));
RtlZeroMemory(
&pv3->ServiceSiteGuid,
sizeof (GUID));
RtlCopyMemory(
ustrp,
pRefList[i].pAddress.Buffer,
pRefList[i].pAddress.Length);
ustrp += pRefList[i].pAddress.Length / sizeof(WCHAR);
*ustrp++ = UNICODE_NULL;
pv3++;
}
DfsReleaseSpcInfo(pFtHashTable, pFtInfo);
ExFreePool(pRefList);
*ReturnedSize = CumulativeSize;
return STATUS_SUCCESS;
}
//+----------------------------------------------------------------------------
//
// Function: DfsFsctrlIsShareInDfs
//
// Synopsis: Determines whether a share path is also in the Dfs.
//
// Arguments: [InputBuffer] -- Pointer to DFS_IS_SHARE_IN_DFS_ARG.
// [InputBufferLength] -- Length in bytes of InputBuffer.
//
// Returns: [STATUS_SUCCESS] -- Share path is in Dfs
//
// [STATUS_NO_SUCH_DEVICE] -- Share path is not in Dfs.
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsFsctrlIsShareInDfs(
IN PVOID InputBuffer,
IN ULONG InputBufferLength)
{
PDFS_IS_SHARE_IN_DFS_ARG arg = (PDFS_IS_SHARE_IN_DFS_ARG) InputBuffer;
PDFS_PKT pkt;
PUNICODE_PREFIX_TABLE_ENTRY lvPrefix;
PDFS_LOCAL_VOL_ENTRY lvEntry;
UNICODE_STRING lvName, remPath;
NTSTATUS status = STATUS_SUCCESS;
//
// Verify the buffer is at least of size DFS_IS_SHARE_IN_DFS_ARG
// And that this is the system process.
//
if (InputBufferLength < sizeof(DFS_IS_SHARE_IN_DFS_ARG)
||
PsGetCurrentProcess() != DfsData.OurProcess
) {
status = STATUS_INVALID_PARAMETER;
DFS_TRACE_HIGH(ERROR, DfsFsctrlIsShareInDfs_Error1, LOGSTATUS(status));
return status;
}
lvName = arg->SharePath;
DebugTrace(+1, Dbg, "DfsFsctrlIsShareInDfs(%wZ)\n", &lvName);
//
// Lookup the localVol in the local volume prefix table.
//
pkt = _GetPkt();
PktAcquireShared( pkt, TRUE );
lvPrefix = DfsFindUnicodePrefix(
&pkt->LocalVolTable,
&lvName,
&remPath);
DebugTrace( 0, Dbg, " lvPrefix=0x%x\n", lvPrefix);
if (lvPrefix != NULL) {
lvEntry = CONTAINING_RECORD(
lvPrefix,
DFS_LOCAL_VOL_ENTRY,
PrefixTableEntry);
DebugTrace( 0, Dbg, " lvEntry=0x%x\n", lvEntry);
DebugTrace( 0, Dbg, " lvEntry->LocalPath=[%wZ]\n", &lvEntry->LocalPath);
if (lvEntry->LocalPath.Length == lvName.Length) {
if (RtlEqualUnicodeString(
&lvEntry->ShareName, &arg->ShareName, TRUE)) {
arg->ShareType = DFS_SHARE_TYPE_DFS_VOLUME;
if (lvEntry->PktEntry->Type & PKT_ENTRY_TYPE_REFERRAL_SVC)
arg->ShareType |= DFS_SHARE_TYPE_ROOT;
status = STATUS_SUCCESS;
} else {
DebugTrace( 0, Dbg, " NO_SUCH_DEVICE(1)\n", 0);
status = STATUS_NO_SUCH_DEVICE;
}
} else {
DebugTrace( 0, Dbg, " NO_SUCH_DEVICE(2)\n", 0);
status = STATUS_NO_SUCH_DEVICE;
}
} else {
DebugTrace( 0, Dbg, " NO_SUCH_DEVICE(3)\n", 0);
status = STATUS_NO_SUCH_DEVICE;
}
PktRelease( pkt );
DebugTrace(-1, Dbg, "DfsFsctrlIsShareInDfs exit 0x%x\n", ULongToPtr( status ));
DFS_TRACE_ERROR_HIGH(status, ALL_ERROR, DfsFsctrlIsShareInDfs_Error2, LOGSTATUS(status));
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: DfsFsctrlFindShare
//
// Synopsis: Determines whether a share path is an FtDFS root
//
// Arguments: [InputBuffer] -- Pointer to DFS_FIND_SHARE_ARG
// [InputBufferLength] -- Length in bytes of InputBuffer.
//
// Returns: [STATUS_PATH_NOT_COVERED] -- Share path is an FtDFS root
// [STATUS_BAD_NETWORK_NAME] -- Share path is NOT an FtDFS root
//
//-----------------------------------------------------------------------------
NTSTATUS
DfsFsctrlFindShare(
IN PVOID InputBuffer,
IN ULONG InputBufferLength)
{
PSPECIAL_HASH_TABLE pFtHashTable = DfsData.FtDfsHashTable;
PDFS_SPECIAL_INFO pFtInfo;
NTSTATUS status = STATUS_SUCCESS;
PDFS_FIND_SHARE_ARG arg = (PDFS_FIND_SHARE_ARG) InputBuffer;
DFS_TRACE_NORM(EVENT, DfsFsctrlFindShare_Start,
LOGSTATUS(status)
LOGUSTR(arg->ShareName));
DebugTrace(+1, Dbg, "DfsFsctrlFindShare(%ws)\n", arg->ShareName.Buffer);
//
// See if we have the FtDfs info cached
//
pFtInfo = DfsLookupSpcInfo(
pFtHashTable,
&arg->ShareName);
if (pFtInfo != NULL) {
if (pFtInfo->NameCount > 0) {
status = STATUS_PATH_NOT_COVERED;
} else {
status = STATUS_BAD_NETWORK_NAME;
}
DfsReleaseSpcInfo(
pFtHashTable,
pFtInfo);
DebugTrace(-1, Dbg, "DfsFsctrlFindShare() -- returning 0x%x\n", ULongToPtr( status ));
DFS_TRACE_ERROR_HIGH(status, ALL_ERROR, DfsFsctrlFindShare_Error1, LOGSTATUS(status));
//return status;
goto cleanup;
}
//
// Not in cache -- call up pipe to dfssvc.exe
//
status = DfsLpcDomRequest(&arg->ShareName);
//
// If success, return STATUS_PATH_NOT_COVERED
if (NT_SUCCESS(status)) {
status = STATUS_PATH_NOT_COVERED;
} else {
status = STATUS_BAD_NETWORK_NAME;
}
DebugTrace(-1, Dbg, "DfsFsctrlFindShare() -- returning 0x%x\n", ULongToPtr( status ));
DFS_TRACE_ERROR_HIGH(status, ALL_ERROR, DfsFsctrlFindShare_Error2, LOGSTATUS(status));
cleanup:
DFS_TRACE_NORM(EVENT, DfsFsctrlFindShare_End,
LOGSTATUS(status)
LOGUSTR(arg->ShareName));
return( status );
}
//+----------------------------------------------------------------------------
//
// Function: SrvShuffle
//
// Synopsis: Shuffles a cost equivalent group of (pointers to) services around
// for load balancing. Uses the classic card shuffling algorithm - for
// each card in the deck, exchange it with a random card in the
// deck.
//
//-----------------------------------------------------------------------------
VOID
SrvShuffle(
PDFS_REFERRAL_LIST pRefList,
LONG nStart,
LONG nEnd)
{
LONG i;
LARGE_INTEGER seed;
//
// We allow caller to have nEnd to be before nStart, so check for this
//
if (nStart >= nEnd)
return;
KeQuerySystemTime( &seed );
for (i = nStart; i <= nEnd; i++) {
DFS_REFERRAL_LIST pTempEntry;
ULONG j;
j = (RtlRandom( &seed.LowPart ) % (nEnd - nStart + 1)) + nStart;
pTempEntry = pRefList[i];
pRefList[i] = pRefList[j];
pRefList[j] = pTempEntry;
}
}
//+----------------------------------------------------------------------------
//
// Function: DfsIpOrdering
//
// Synopsis: Reorders a list of names based upon the passed-in client ip address.
//
//-----------------------------------------------------------------------------
ULONG
DfsIpOrdering(
IN PDFS_IP_INFO pIpInfo,
IN ULONG RefCount,
IN PDFS_REFERRAL_LIST pRefList)
{
PDFS_REFERRAL_LIST pOrdRefList;
PDFS_SITE_INFO pSiteInfo;
BOOLEAN ToFront;
LONG Front = RefCount;
LONG Back;
ULONG i, j;
DebugTrace(+1, Dbg, "DfsIpOrdering()\n", 0);
//
// Create an ordered list of addresses based on Sites
//
pOrdRefList = ExAllocatePoolWithTag(
PagedPool,
sizeof(DFS_REFERRAL_LIST) * RefCount,
' sfD');
if (pOrdRefList != NULL) {
Front = 0;
Back = RefCount - 1;
for (i = 0; i < RefCount; i++) {
//
// Scan the list of alternates, placing each alternate either toward the
// front of the ordered list (if site matches) or toward the rear of the
// ordered list (if sites don't match).
//
ToFront = FALSE;
pSiteInfo = DfsLookupSiteInfo(&pRefList[i].pName);
if (pSiteInfo != NULL) {
for (j = 0; ToFront == FALSE && j < pSiteInfo->SiteCount; j++) {
if (RtlCompareUnicodeString(
&pIpInfo->SiteName,
&pSiteInfo->SiteName[j],
TRUE) == 0) {
ToFront = TRUE;
}
}
DfsReleaseSiteInfo(pSiteInfo);
}
if (ToFront == TRUE) {
pOrdRefList[Front++] = pRefList[i];
} else {
pOrdRefList[Back--] = pRefList[i];
}
}
//
// Replace the unordered list with the ordered one
//
for (i = 0; i < RefCount; i++) {
pRefList[i] = pOrdRefList[i];
}
ExFreePool(pOrdRefList);
}
DebugTrace(-1, Dbg, "DfsIpOrdering()--exit\n", 0);
return Front;
}
//+----------------------------------------------------------------------------
//
// Function: DfspLookupSpcEntry
//
// Synopsis: Returns a special list info entry, expanded if necessary
//
//-----------------------------------------------------------------------------
PDFS_SPECIAL_INFO
DfspLookupSpcEntry(
IN PUNICODE_STRING SpecialName)
{
PSPECIAL_HASH_TABLE pHashTable = DfsData.SpcHashTable;
PDFS_SPECIAL_INFO pSpcInfo = NULL;
LARGE_INTEGER now;
ULONG TypeFlags;
DebugTrace(+1, Dbg, "DfspLookupSpcEntry(%wZ)\n", SpecialName);
//
// Check the SpcName cache
//
pSpcInfo = DfsLookupSpcInfo(
pHashTable,
SpecialName);
if (pSpcInfo != NULL) {
//
// If the entry is old or unexpanded, try to expand it
//
KeQuerySystemTime(&now);
if ((now.QuadPart > pSpcInfo->ExpireTime.QuadPart ||
pSpcInfo->NameCount == -1)
) {
ExAcquireFastMutex( &pHashTable->HashListMutex );
if (now.QuadPart > pSpcInfo->ExpireTime.QuadPart) {
pSpcInfo->ExpireTime.QuadPart = now.QuadPart + UInt32x32To64(
60 * 60,
10 * 1000 * 1000);
TypeFlags = pSpcInfo->TypeFlags;
ExReleaseFastMutex( &pHashTable->HashListMutex );
DfsReleaseSpcInfo(pHashTable, pSpcInfo);
//
// Try to refresh the cache
//
if (pSpcInfo->Flags & SPECIAL_INFO_IS_LONG_NAME) {
pSpcInfo->Flags |= SPECIAL_INFO_NEEDS_REFRESH;
}
DfsLpcSpcRequest(SpecialName, TypeFlags);
//
// And try again
//
pSpcInfo = DfsLookupSpcInfo(
pHashTable,
SpecialName);
} else {
ExReleaseFastMutex( &pHashTable->HashListMutex );
}
}
if (pSpcInfo != NULL) {
if (pSpcInfo->NameCount == -1) {
DfsReleaseSpcInfo(pHashTable, pSpcInfo);
pSpcInfo = NULL;
}
}
}
DebugTrace(-1, Dbg, "DfspLookupSpcEntry returning 0x%x\n", pSpcInfo);
return pSpcInfo;
}
//+----------------------------------------------------------------------------
//
// Function: DfspIsSpecialShare, local
//
// Synopsis: Sees if a share name is a special share.
//
// Arguments: [ShareName] -- Name of share to test.
//
// Returns: TRUE if special, FALSE otherwise.
//
//-----------------------------------------------------------------------------
BOOLEAN
DfspIsSpecialShare(
PUNICODE_STRING ShareName)
{
ULONG i;
BOOLEAN fSpecial = FALSE;
for (i = 0;
(i < (sizeof(SpecialShares) / sizeof(SpecialShares[0]))) &&
!fSpecial;
i++) {
if (SpecialShares[i].Length == ShareName->Length) {
if (RtlCompareUnicodeString(&SpecialShares[i],ShareName,TRUE) == 0) {
fSpecial = TRUE;
}
}
}
return( fSpecial );
}