Leaked source code of windows server 2003
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

/*++
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;
}