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.
848 lines
28 KiB
848 lines
28 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
cscdfs.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines for integrating DFS funcitionality with
|
|
CSC
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman [SethuR] 23 - January - 1998
|
|
|
|
Revision History:
|
|
|
|
Notes:
|
|
|
|
The CSC/DFS integration poses some interesting problem for transparent
|
|
switchover from online to offline operation and viceversa.
|
|
|
|
The DFS driver ( incorporated into MUP in its current incarnation ) builds
|
|
up a logical name space by stitching together varios physical volumes on
|
|
different machines. In the normal course of events the CSC framework will
|
|
be above the DFS driver in the food chain. In such cases this module
|
|
would be redundant and the entire process is contained in the CSC code.
|
|
Since this is not an available option owing to the necessity of integrating
|
|
the CSC code in its present form this module tries to seamlessly provide
|
|
that functionality.
|
|
|
|
The DFS driver uses specially defined IOCTL's over the SMB protocol to garner
|
|
and disperse DFS state information. The SMB protocol contains additional
|
|
flag definitions that communicate the type of shares ( DFS aware or otherwise)
|
|
to the clients. This information is stored in the redirector data structures.
|
|
The DFS driver uses the FsContext2 field of the file object passed to it in
|
|
Create calls to communicate the fact that it is a DFS open to the underlying
|
|
redirector. This is used to validate opens in the redirector.
|
|
|
|
With the introduction of CSC as part of the redirector there are some new
|
|
wrinkles in the system. Since the DFS driver munges the names passed to it
|
|
the name seen by the redirector is very different from the passed in by the
|
|
user. If the CSC were to base its database based upon the names passed to
|
|
the redirector then a DFS will be required to operate in the offline case.
|
|
The DFS driver will persistently store its PKT data structure and do the same
|
|
name munging in the offline case. With the introduction of fault tolerant DFS
|
|
maintaining the state consistently and persistently will be a very tough
|
|
problem.
|
|
|
|
Another option to consider would be that the CSC database will be maintained
|
|
in terms of the path names passed in by the user and not the munged names
|
|
passed in by the redirector. This would imply that in the offline state the
|
|
CSC code can independently exist to serve files off the DFS namespace without
|
|
the DFS driver. The current solution that has been implemented for integrating
|
|
CSC and DFS is based on this premise.
|
|
|
|
In order to make this work we need to establish a mechanism for DFS to pass
|
|
in the original name passed into DFS. This is done by usurping the FsContext
|
|
field in the file object associated with the IRP that is passed in. DFS
|
|
fills in this field with an instance of a DFS defined data structure
|
|
(DFS_CSC_NAME_CONTEXT). This data structure contains the original name
|
|
passed into DFS alonwith an indication of whether the open was triggerred by
|
|
the CSC agent. The DFS driver is responsible for managing the storage
|
|
associated with these contexts. When the DFS driver hands over the IRP this
|
|
value is filled in and must remain valid till the IRP is completed. On
|
|
completion of the IRP the context field would have been overridden by the
|
|
underlying driver. The DFS driver cannot rely on the context fields having
|
|
the value on return and must take adequate steps to squirrel away the data
|
|
in its data structures.
|
|
|
|
Having thus established the mechanism for DFS to pass in the original name
|
|
the next step is to outline the mechanisms for CSC to use it. Since the CSC
|
|
database is organized in terms of server/share names we will limit the
|
|
discussion to shares with the implicit understanding that the extension to
|
|
all file names in the name space is trivial.
|
|
|
|
The DFS shares that are established for accessing files/folders can be classified
|
|
into four categories based on their lifetimes
|
|
|
|
1) DFS Share accessed in connected mode only
|
|
|
|
2) DFS Share accessed in disconnected mode only
|
|
|
|
3) DFS Share established in connected mode but is accessed after transitioning to
|
|
disconnected mode
|
|
|
|
4) DFS Share established in disconnected mode but is accessed after transitioning
|
|
to connected mode.
|
|
|
|
In the case of connected mode operation we need to ensure that the redirector
|
|
consistently manipulates the RDR data structures in terms of the munged name
|
|
passed in by the DFS driver and the CSC database in terms of the original
|
|
name passed in by the user. As an example consider the following name
|
|
|
|
\\DfsRoot\DfsShare\foo\bar passed in by the user. This name can be
|
|
|
|
munged into one of three possible names by the DFS driver.
|
|
|
|
TBD -- fill in appropriate FTDFS, middle level volume and leaf volume
|
|
munged name.
|
|
|
|
In the current RDR data structures the NET_ROOT_ENTRY claims the first two
|
|
components of a name for the CSC database structures. There are two instances
|
|
of CSC database handles for the Server ( server and share name in UNC
|
|
terminology ) and the root directory. These names were driven off the name
|
|
as seen by the redirector ( munged name ). In the modified scheme these will
|
|
be driven off the original name passed in by the user. This ensures that no
|
|
other code path need be changed in the redirector to facilitate DFS/CSC
|
|
integration.
|
|
|
|
In disconnected mode the names as passed in by the user is passed into the
|
|
redirector which passes the appropriate components to CSC. Since CSC is
|
|
maintained in terms of original names it works as before.
|
|
|
|
When the user has modified files on a DFS share it is necessary to ensure
|
|
that the DFS name space munging/resolution does not come into play till
|
|
the CSC agent has had a chance to ensure that the files have been integrated
|
|
This is accomplished by having the redirector preprocess the DFS open requests
|
|
in connected mode. If the share has been marked dirty in the CSC database
|
|
the redirector munges the name on its own to bypass DFS name resolution and
|
|
returns STATUS_REPARSE to the DFS driver.
|
|
|
|
There is one other situation which needs special treatment. A DFS server/share
|
|
cannot be transitioned to disconnected operation by the redirector. Since
|
|
the DFS name space is built out of multiple options with a variety of fault
|
|
tolerant options the inability to open a file/contact a server in the DFS
|
|
namespace cannot lead to transitioning in the redirector. It is left to
|
|
the discretion of the DFS driver to implement the appropriate mechanism
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
NTSTATUS
|
|
CscDfsParseDfsPath(
|
|
PUNICODE_STRING pDfsPath,
|
|
PUNICODE_STRING pServerName,
|
|
PUNICODE_STRING pSharePath,
|
|
PUNICODE_STRING pFilePathRelativeToShare)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses the DFS path into a share path and a file path
|
|
|
|
Arguments:
|
|
|
|
pDfsPath -- the DFS path
|
|
|
|
pServerName -- the name of the server
|
|
|
|
pSharePath -- the share path
|
|
|
|
pFilePathRelativeToShare -- file path relative to share
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS -- successful
|
|
|
|
STATUS_OBJECT_PATH_INVALID -- the path supplied was invalid
|
|
|
|
Notes:
|
|
|
|
Either pServerName or pSharePath or pFilePathRelativeToShare can be NULL
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PWCHAR pTempPathString;
|
|
|
|
ULONG NumberOfBackSlashesSkipped;
|
|
ULONG TempPathLengthInChars;
|
|
ULONG SharePathLengthInChars;
|
|
|
|
pTempPathString = pDfsPath->Buffer;
|
|
TempPathLengthInChars = pDfsPath->Length / sizeof(WCHAR);
|
|
|
|
if (pServerName != NULL) {
|
|
pServerName->Length = pServerName->MaximumLength = 0;
|
|
pServerName->Buffer = NULL;
|
|
}
|
|
|
|
if (pSharePath != NULL) {
|
|
pSharePath->Length = pSharePath->MaximumLength = 0;
|
|
pSharePath->Buffer = NULL;
|
|
}
|
|
|
|
if (pFilePathRelativeToShare != NULL) {
|
|
pFilePathRelativeToShare->Length = pFilePathRelativeToShare->MaximumLength = 0;
|
|
pFilePathRelativeToShare->Buffer = NULL;
|
|
}
|
|
|
|
// Skip till after the third backslash or the end of the name
|
|
// whichever comes first.
|
|
// The name we are interested in is of the form \server\share\
|
|
// with the last backslash being optional.
|
|
|
|
NumberOfBackSlashesSkipped = 0;
|
|
SharePathLengthInChars = 0;
|
|
|
|
while (TempPathLengthInChars > 0) {
|
|
if (*pTempPathString++ == L'\\') {
|
|
NumberOfBackSlashesSkipped++;
|
|
|
|
if (NumberOfBackSlashesSkipped == 2) {
|
|
if (pServerName != NULL) {
|
|
pServerName->Length = (USHORT)SharePathLengthInChars * sizeof(WCHAR);
|
|
pServerName->MaximumLength = pServerName->Length;
|
|
pServerName->Buffer = pDfsPath->Buffer;
|
|
}
|
|
} else if (NumberOfBackSlashesSkipped == 3) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
TempPathLengthInChars--;
|
|
SharePathLengthInChars++;
|
|
}
|
|
|
|
if (NumberOfBackSlashesSkipped >= 2) {
|
|
if (pSharePath != NULL) {
|
|
pSharePath->Length = (USHORT)SharePathLengthInChars * sizeof(WCHAR);
|
|
pSharePath->MaximumLength = pSharePath->Length;
|
|
pSharePath->Buffer = pDfsPath->Buffer;
|
|
}
|
|
|
|
if ((pFilePathRelativeToShare != NULL) &&
|
|
(NumberOfBackSlashesSkipped == 3)) {
|
|
pFilePathRelativeToShare->Length =
|
|
pDfsPath->Length - ((USHORT)SharePathLengthInChars * sizeof(WCHAR));
|
|
pFilePathRelativeToShare->MaximumLength = pFilePathRelativeToShare->Length;
|
|
pFilePathRelativeToShare->Buffer = pDfsPath->Buffer + SharePathLengthInChars;
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
} else {
|
|
Status = STATUS_OBJECT_PATH_INVALID;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
PDFS_NAME_CONTEXT
|
|
CscIsValidDfsNameContext(
|
|
PVOID pFsContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the supplied context is a valid DFS_NAME_CONTEXT
|
|
|
|
Arguments:
|
|
|
|
pFsContext - the supplied context
|
|
|
|
Return Value:
|
|
|
|
valid context ponter if the supplied context is a valid DFS_NAME_CONTEXT
|
|
instance, otherwise NULL
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PDFS_NAME_CONTEXT pValidDfsNameContext = NULL;
|
|
|
|
if (pFsContext != NULL) {
|
|
pValidDfsNameContext = pFsContext;
|
|
}
|
|
|
|
return pValidDfsNameContext;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscGrabPathFromDfs(
|
|
PFILE_OBJECT pFileObject,
|
|
PDFS_NAME_CONTEXT pDfsNameContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine modifies the file object in preparation for returning
|
|
STATUS_REPARSE
|
|
|
|
Arguments:
|
|
|
|
pFileObject - the file object
|
|
|
|
pDfsNameContext - the DFS_NAME_CONTEXT instance
|
|
|
|
Return Value:
|
|
|
|
STATUS_REPARSE if everything is successful
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
USHORT DeviceNameLength,ReparsePathLength;
|
|
PWSTR pFileNameBuffer;
|
|
|
|
DeviceNameLength = wcslen(DD_NFS_DEVICE_NAME_U) *
|
|
sizeof(WCHAR);
|
|
|
|
|
|
ReparsePathLength = DeviceNameLength +
|
|
pDfsNameContext->UNCFileName.Length;
|
|
|
|
if (pDfsNameContext->UNCFileName.Buffer[0] != L'\\') {
|
|
ReparsePathLength += sizeof(WCHAR);
|
|
}
|
|
|
|
pFileNameBuffer = RxAllocatePoolWithTag(
|
|
PagedPool | POOL_COLD_ALLOCATION,
|
|
ReparsePathLength,
|
|
RX_MISC_POOLTAG);
|
|
|
|
if (pFileNameBuffer != NULL) {
|
|
// Copy the device name
|
|
RtlCopyMemory(
|
|
pFileNameBuffer,
|
|
DD_NFS_DEVICE_NAME_U,
|
|
DeviceNameLength);
|
|
|
|
if (pDfsNameContext->UNCFileName.Buffer[0] != L'\\') {
|
|
DeviceNameLength += sizeof(WCHAR);
|
|
|
|
pFileNameBuffer[DeviceNameLength/sizeof(WCHAR)]
|
|
= L'\\';
|
|
}
|
|
|
|
// Copy the new name
|
|
RtlCopyMemory(
|
|
((PBYTE)pFileNameBuffer +
|
|
DeviceNameLength),
|
|
pDfsNameContext->UNCFileName.Buffer,
|
|
pDfsNameContext->UNCFileName.Length);
|
|
|
|
if (pFileObject->FileName.Buffer != NULL)
|
|
ExFreePool(pFileObject->FileName.Buffer);
|
|
|
|
pFileObject->FileName.Buffer = pFileNameBuffer;
|
|
pFileObject->FileName.Length = ReparsePathLength;
|
|
pFileObject->FileName.MaximumLength =
|
|
pFileObject->FileName.Length;
|
|
|
|
Status = STATUS_REPARSE;
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscPreProcessCreateIrp(
|
|
PIRP pIrp)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if the CSC/Redirector should defeat the DFS
|
|
name resolution scheme
|
|
|
|
Arguments:
|
|
|
|
pIrp - the IRP
|
|
|
|
Return Value:
|
|
|
|
This routine bases this determination on whether there are dirty files
|
|
corresponding to this DFS share name. If there are the name is claimed by
|
|
the redirector by changing the file name to the new value and returning
|
|
STATUS_REPARSE. In all other cases STATUS_SUCCESS is returned.
|
|
|
|
Notes:
|
|
|
|
This routine assumes that the name passed in the DFS_NAME_CONTEXT is a
|
|
name in the following format irrespective of whether the user specifies a
|
|
drive letter based name or a UNC name.
|
|
|
|
\DfsRoot\DfsShare\ ....
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
PFILE_OBJECT pFileObject;
|
|
PDFS_NAME_CONTEXT pDfsNameContext;
|
|
|
|
if(!MRxSmbIsCscEnabled ||
|
|
!fShadow
|
|
) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
|
|
|
|
pFileObject = pIrpSp->FileObject;
|
|
|
|
pDfsNameContext = CscIsValidDfsNameContext(pFileObject->FsContext);
|
|
|
|
if ((pDfsNameContext != NULL) &&
|
|
(pDfsNameContext->NameContextType != DFS_CSCAGENT_NAME_CONTEXT)) {
|
|
UNICODE_STRING ShareName;
|
|
UNICODE_STRING ServerName;
|
|
|
|
Status = CscDfsParseDfsPath(
|
|
&pDfsNameContext->UNCFileName,
|
|
&ServerName,
|
|
&ShareName,
|
|
NULL);
|
|
|
|
// Locate the share name / server name in the database.
|
|
if (Status == STATUS_SUCCESS) {
|
|
// At this stage the given path name has been parsed into the
|
|
// relevant components, i.e., the server name, the share name
|
|
// ( also includes the server name ) and the file name relative
|
|
// to the share. Of these the first two components are valuable
|
|
// in determining whether the given path has to be grabbed from
|
|
// DFS.
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
// If a server entry exists in the connection engine database
|
|
// for the given server name and it had been marked for
|
|
// disconnected operation then the path needs to be grabbed from
|
|
// DFS. This will take into account those cases wherein there
|
|
// is some connectivity but the DFS root has been explicitly
|
|
// marked for disconnected operation.
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
SmbCeAcquireResource();
|
|
|
|
pServerEntry = SmbCeFindServerEntry(
|
|
&ServerName,
|
|
SMBCEDB_FILE_SERVER,
|
|
NULL);
|
|
|
|
SmbCeReleaseResource();
|
|
|
|
if (pServerEntry != NULL) {
|
|
if (SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
|
Status = STATUS_REPARSE;
|
|
}
|
|
|
|
SmbCeDereferenceServerEntry(pServerEntry);
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
|
|
// If no server entry exists or it has not been transitioned into
|
|
// disconnected mode we check the CSC database for an entry
|
|
// corresponding to the given share name. If one exists and there
|
|
// are no transports available then the name needs to be grabbed.
|
|
// This takes into account all the cases wherein there is no
|
|
// net connectivity.
|
|
|
|
SHADOWINFO ShadowInfo;
|
|
DWORD CscServerStatus;
|
|
|
|
EnterShadowCrit();
|
|
|
|
CscServerStatus = FindCreateShareForNt(
|
|
&ShareName,
|
|
FALSE,
|
|
&ShadowInfo,
|
|
NULL);
|
|
|
|
LeaveShadowCrit();
|
|
|
|
if ( CscServerStatus == SRET_OK ) {
|
|
PSMBCE_TRANSPORT_ARRAY pTransportArray;
|
|
|
|
// If an entry corresponding to the dfs root exists in the
|
|
// CSC database transition to using it either if it is marked
|
|
// dirty or there are no transports.
|
|
|
|
pTransportArray = SmbCeReferenceTransportArray();
|
|
SmbCeDereferenceTransportArray(pTransportArray);
|
|
|
|
if (pTransportArray == NULL) {
|
|
Status = STATUS_REPARSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the status value is STATUS_REPARSE one of the rule applications
|
|
// was successful and we do the appropriate name munging to grab
|
|
// the DFS path
|
|
// If the status value is still STATUS_MORE_PROCESSING_REQUIRED
|
|
// it implies that none of our rules for reparsing the DFS path
|
|
// was successful. We map the status back to STATUS_SUCCESS to
|
|
// resume the connected mode of operation
|
|
|
|
if (Status == STATUS_REPARSE) {
|
|
if (pDfsNameContext->Flags & DFS_FLAG_LAST_ALTERNATE) {
|
|
Status = CscGrabPathFromDfs(
|
|
pFileObject,
|
|
pDfsNameContext);
|
|
} else {
|
|
Status = STATUS_NETWORK_UNREACHABLE;
|
|
}
|
|
} else if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscDfsDoDfsNameMapping(
|
|
IN PUNICODE_STRING pDfsPrefix,
|
|
IN PUNICODE_STRING pActualPrefix,
|
|
IN PUNICODE_STRING pNameToMap,
|
|
IN BOOL fResolvedNameToDFSName,
|
|
OUT PUNICODE_STRING pResult
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given the DfsPrefix and it's corresponding actualprefix, this routine maps a resolved
|
|
name to the corresponding DFS name or viceversa
|
|
|
|
As an example if \\csctest\dfs\ntfs is actually resolved to \\dkruse1\ntfs
|
|
then \\csctest\dfs\ntfs\dir1\foo.txt resolves to \\dkruse1\ntfs\dir1\foo.txt.
|
|
|
|
The DFS prefix in this case is \ntfs and the Actual prefix is \.
|
|
|
|
Thus, given an actual path \dir1\foo.txt, this routine reverse maps it to
|
|
\ntfs\dir1\foo.txt or viceversa
|
|
|
|
|
|
Arguments:
|
|
|
|
pDfsPrefix Dfs Name prefix
|
|
|
|
pActaulPrefix Corresponding actual prefix
|
|
|
|
pNameToMap Actual name
|
|
|
|
fResolvedNameToDFSName If true, we are converting resolved name to DFS name, else viceversa
|
|
|
|
pResult Output DFS name
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, NT error code otherwise
|
|
|
|
Notes:
|
|
|
|
|
|
This routine is used by CSC to obtain a DFS name from an actual name or viceversa. It is used to get the
|
|
DFS name of a rename name so that the CSC database can do the manipulations in terms
|
|
of the DFS names. It is also used to get the info from the server using the real name
|
|
while createing entries in the CSC database in the DFS namespace.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_NO_SUCH_FILE;
|
|
PUNICODE_STRING pSourcePrefix=pActualPrefix , pDestPrefix=pDfsPrefix;
|
|
UNICODE_STRING NameToCopy;
|
|
|
|
memset(pResult, 0, sizeof(UNICODE_STRING));
|
|
|
|
if (fResolvedNameToDFSName)
|
|
{
|
|
// converting resolved name to DFS name
|
|
pSourcePrefix=pActualPrefix;
|
|
pDestPrefix=pDfsPrefix;
|
|
}
|
|
else
|
|
{
|
|
// converting DFS name to resolved name
|
|
pSourcePrefix=pDfsPrefix;
|
|
pDestPrefix=pActualPrefix;
|
|
|
|
}
|
|
// DbgPrint("CscDoDfsnamemapping: DestPrefix=%wZ SourcePrefix=%wZ NameToMap=%wZ\n",
|
|
// pDestPrefix, pSourcePrefix, pNameToMap);
|
|
|
|
//mathc the prefix
|
|
if (RtlPrefixUnicodeString(pSourcePrefix, pNameToMap, TRUE))
|
|
{
|
|
ASSERT(pNameToMap->Length >= pSourcePrefix->Length);
|
|
|
|
// Calculate the max length.
|
|
pResult->MaximumLength = pDestPrefix->Length + pNameToMap->Length - pSourcePrefix->Length+2;
|
|
|
|
pResult->Buffer = (PWCHAR)AllocMem(pResult->MaximumLength);
|
|
|
|
if (pResult->Buffer)
|
|
{
|
|
// set the initial length
|
|
pResult->Length = pDestPrefix->Length;
|
|
memcpy(pResult->Buffer, pDestPrefix->Buffer, pDestPrefix->Length);
|
|
|
|
// if there isn't a terminating backslash, put it in there
|
|
if (pResult->Buffer[pResult->Length/sizeof(USHORT)-1] != L'\\')
|
|
{
|
|
pResult->Buffer[pResult->Length/sizeof(USHORT)] = L'\\';
|
|
pResult->Length += 2;
|
|
|
|
}
|
|
|
|
NameToCopy.Buffer = pNameToMap->Buffer+pSourcePrefix->Length/sizeof(USHORT);
|
|
NameToCopy.Length = pNameToMap->Length-pSourcePrefix->Length;
|
|
|
|
// DbgPrint("CscDoDfsNameMapping: Copying %wZ\n", &NameToCopy);
|
|
|
|
// now copy the nametomap without the sourceprefix, into the buffer
|
|
memcpy( pResult->Buffer+pResult->Length/sizeof(USHORT),
|
|
NameToCopy.Buffer,
|
|
NameToCopy.Length);
|
|
|
|
pResult->Length += NameToCopy.Length;
|
|
|
|
ASSERT(pResult->Length <= pResult->MaximumLength);
|
|
|
|
// DbgPrint("CscDoDfsNameMapping: pResult %wZ\n", pResult);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscDfsObtainReverseMapping(
|
|
IN PUNICODE_STRING pDfsPath,
|
|
IN PUNICODE_STRING pResolvedPath,
|
|
OUT PUNICODE_STRING pReverseMappingDfs,
|
|
OUT PUNICODE_STRING pReverseMappingActual)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine obtains the mapping strings which allow csc to map a resolved path
|
|
to a DFS path
|
|
|
|
As an example if \\csctest\dfs\ntfs is actually resolved to \\dkruse1\ntfs
|
|
then \\csctest\dfs\ntfs\dir1\foo.txt resolves to \\dkruse1\ntfs\dir1\foo.txt.
|
|
|
|
The DFS prefix in this case is \ntfs and the Actual prefix is \.
|
|
|
|
Arguments:
|
|
|
|
pDfsPath Path relative to the root of the DFS share
|
|
|
|
pResolvedPath Path relative to the actual share
|
|
|
|
pReverseMappingDfs
|
|
|
|
pReverseMappingActual
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, NT error code otherwise
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status=STATUS_INSUFFICIENT_RESOURCES;
|
|
PWCHAR pDfs, pActual;
|
|
WCHAR wDfs, wActual;
|
|
DWORD cbDfs=0, cbActual=0;
|
|
UNICODE_STRING uniSavResolved, uniSavDfs;
|
|
BOOL fSavedResolved = FALSE, fSavedDfs = FALSE;
|
|
|
|
|
|
// take care of the root
|
|
if (pResolvedPath->Length == 0)
|
|
{
|
|
uniSavResolved = *pResolvedPath;
|
|
pResolvedPath->Length = pResolvedPath->MaximumLength = 2;
|
|
pResolvedPath->Buffer = L"\\";
|
|
fSavedResolved = TRUE;
|
|
}
|
|
// take care of the root
|
|
if (pDfsPath->Length == 0)
|
|
{
|
|
uniSavDfs = *pDfsPath;
|
|
pDfsPath->Length = pDfsPath->MaximumLength = 2;
|
|
pDfsPath->Buffer = L"\\";
|
|
fSavedDfs = TRUE;
|
|
}
|
|
|
|
memset(pReverseMappingDfs, 0, sizeof(UNICODE_STRING));
|
|
memset(pReverseMappingActual, 0, sizeof(UNICODE_STRING));
|
|
|
|
// point to the end of each string
|
|
pDfs = (PWCHAR)((LPBYTE)(pDfsPath->Buffer)+pDfsPath->Length - 2);
|
|
cbDfs = pDfsPath->Length;
|
|
|
|
pActual = (PWCHAR)((LPBYTE)(pResolvedPath->Buffer)+pResolvedPath->Length - 2);
|
|
cbActual = pResolvedPath->Length;
|
|
|
|
// DbgPrint("CscDfsObtainReverseMapping: In DfsPath=%wZ ResolvedPath=%wZ\n", pDfsPath, pResolvedPath);
|
|
|
|
pReverseMappingDfs->MaximumLength = pReverseMappingDfs->Length = (USHORT)cbDfs;
|
|
pReverseMappingActual->MaximumLength = pReverseMappingActual->Length = (USHORT)cbActual;
|
|
|
|
for (;;)
|
|
{
|
|
// do an upcase comparison
|
|
wDfs = RtlUpcaseUnicodeChar(*pDfs);
|
|
wActual = RtlUpcaseUnicodeChar(*pActual);
|
|
|
|
if (wDfs != wActual)
|
|
{
|
|
ASSERT(pReverseMappingDfs->Length && pReverseMappingActual->Length);
|
|
break;
|
|
}
|
|
|
|
// if we reached a backslash, checkpoint the path upto this point
|
|
if (wDfs == (WCHAR)'\\')
|
|
{
|
|
pReverseMappingDfs->MaximumLength = pReverseMappingDfs->Length = (USHORT)cbDfs;
|
|
pReverseMappingActual->MaximumLength = pReverseMappingActual->Length = (USHORT)cbActual;
|
|
}
|
|
|
|
if ((pDfs == pDfsPath->Buffer)||(pActual == pResolvedPath->Buffer))
|
|
{
|
|
break;
|
|
}
|
|
|
|
--pDfs;--pActual;
|
|
cbDfs -= 2; cbActual -= 2;
|
|
}
|
|
|
|
pReverseMappingDfs->Buffer = (PWCHAR)RxAllocatePoolWithTag(NonPagedPool, pReverseMappingDfs->Length, RX_MISC_POOLTAG);
|
|
|
|
if (pReverseMappingDfs->Buffer)
|
|
{
|
|
pReverseMappingActual->Buffer = (PWCHAR)RxAllocatePoolWithTag(NonPagedPool, pReverseMappingActual->Length, RX_MISC_POOLTAG);
|
|
|
|
if (pReverseMappingActual->Buffer)
|
|
{
|
|
|
|
memcpy(pReverseMappingDfs->Buffer, pDfsPath->Buffer, pReverseMappingDfs->Length);
|
|
memcpy(pReverseMappingActual->Buffer, pResolvedPath->Buffer, pReverseMappingActual->Length);
|
|
|
|
// DbgPrint("CscDfsObtainReverseMapping: out DfsPrefix=%wZ ActualPrefix=%wZ\n", pReverseMappingDfs, pReverseMappingActual);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
{
|
|
if (pReverseMappingDfs->Buffer)
|
|
{
|
|
FreeMem(pReverseMappingDfs->Buffer);
|
|
}
|
|
pReverseMappingDfs->Buffer = NULL;
|
|
|
|
if (pReverseMappingActual->Buffer)
|
|
{
|
|
FreeMem(pReverseMappingActual->Buffer);
|
|
}
|
|
pReverseMappingActual->Buffer = NULL;
|
|
}
|
|
|
|
if (fSavedResolved)
|
|
{
|
|
*pResolvedPath = uniSavResolved;
|
|
}
|
|
if (fSavedDfs)
|
|
{
|
|
*pDfsPath = uniSavDfs;
|
|
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
CscDfsStripLeadingServerShare(
|
|
IN PUNICODE_STRING pDfsRootPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine strips the leading server-share of a DFS root path. Thus when Dfs sends down a path
|
|
relative to the root such as \server\share\foo.txt, this routien makes the path \foo.txt
|
|
|
|
Arguments:
|
|
|
|
pDfsRootPath Path relative to the root of the DFS share
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful, NT error code otherwise
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG cnt = 0, i;
|
|
|
|
DbgPrint("Stripping %wZ \n", pDfsRootPath);
|
|
for (i=0; ;++i)
|
|
{
|
|
if (i*sizeof(WCHAR) > pDfsRootPath->Length)
|
|
{
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
if (pDfsRootPath->Buffer[i] == '\\')
|
|
{
|
|
// if this is the 3rd slash then we have successfully stripped the name
|
|
if (cnt == 2)
|
|
{
|
|
break;
|
|
}
|
|
cnt++;
|
|
}
|
|
}
|
|
pDfsRootPath->Buffer = &pDfsRootPath->Buffer[i];
|
|
pDfsRootPath->Length -= (USHORT)(i * sizeof(WCHAR));
|
|
DbgPrint("Stripped name %wZ \n", pDfsRootPath);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|