/*++

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;
}