//+----------------------------------------------------------------------------
//
//  Copyright (C) 1996, Microsoft Corporation
//
//  File:       dfsext.c
//
//  Contents:   Code to see if a path refers to a Dfs path.
//
//  Classes:    None
//
//  Functions:  IsThisADfsPath
//
//  History:    March 11, 1996  Milans created
//
//-----------------------------------------------------------------------------

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <dfsfsctl.h>
#include <windows.h>

NTSTATUS
DfsFsctl(
    IN  HANDLE DfsHandle,
    IN  ULONG FsControlCode,
    IN  PVOID InputBuffer OPTIONAL,
    IN  ULONG InputBufferLength,
    OUT PVOID OutputBuffer OPTIONAL,
    IN  ULONG OutputBufferLength);

NTSTATUS
DfsOpen(
    IN  OUT PHANDLE DfsHandle);

//+----------------------------------------------------------------------------
//
//  Function:   IsThisADfsPath, public
//
//  Synopsis:   Given a fully qualified UNC or Drive based path, this routine
//              will identify if it is a Dfs path or not.
//
//  Arguments:  [pwszPath] -- The fully qualified path to test.
//
//              [cwPath] -- Length, in WCHARs, of pwszPath. If this is 0,
//                      this routine will compute the length. If it is
//                      non-zero, it will assume that the length of pwszPath
//                      is cwPath WCHARs.
//
//  Returns:    TRUE if pwszPath is a Dfs path, FALSE otherwise.
//
//-----------------------------------------------------------------------------

BOOL
IsThisADfsPath(
    IN LPCWSTR pwszPath,
    IN DWORD cwPath OPTIONAL)
{
    NTSTATUS Status;
    HANDLE hDfs;
    BOOL fIsDfsPath = FALSE;
    PDFS_IS_VALID_PREFIX_ARG pPrefixArg;
    ULONG Size;

    //
    // We only accept UNC or drive letter paths
    //

    if (pwszPath == NULL)
        return( FALSE );

    if (cwPath == 0)
        cwPath = wcslen( pwszPath );

    if (cwPath < 2)
        return( FALSE );

    Status = DfsOpen( &hDfs );

    if (!NT_SUCCESS(Status))
        return( FALSE );

    //
    // From this point on, we must remember to close hDfs before returning.
    //

    if (pwszPath[0] == L'\\' && pwszPath[1] == L'\\') {

        Size = sizeof(DFS_IS_VALID_PREFIX_ARG) +
                    cwPath * sizeof(WCHAR);

        pPrefixArg = (PDFS_IS_VALID_PREFIX_ARG) LocalAlloc(0, Size);

        if ( pPrefixArg ) {

            //
            // the InputBuffer must be in the structure of DFS_IS_VALID_PREFIX_ARG
            //

            pPrefixArg->CSCAgentCreate = FALSE;
            pPrefixArg->RemoteNameLen = (SHORT)( (cwPath-1) * sizeof(WCHAR));
            wcscpy(&pPrefixArg->RemoteName[0], pwszPath+1);

            Status = DfsFsctl(
                        hDfs,
                        FSCTL_DFS_IS_VALID_PREFIX,
                        (PVOID) pPrefixArg, // &pwszPath[1],
                        Size, // (cwPath - 1) * sizeof(WCHAR),
                        NULL,
                        0);

            LocalFree(pPrefixArg);

        } else {
            Status = STATUS_NO_MEMORY;
        }

        if (NT_SUCCESS(Status))
            fIsDfsPath = TRUE;

    } else if (pwszPath[1] == L':') {

        //
        // This is a drive based name. We'll fsctl to the driver to return
        // the prefix for this drive, if it is indeed a Dfs drive.
        //

        Status = DfsFsctl(
                    hDfs,
                    FSCTL_DFS_IS_VALID_LOGICAL_ROOT,
                    (PVOID) &pwszPath[0],
                    sizeof(WCHAR),
                    NULL,
                    0);

        if (NT_SUCCESS(Status))
            fIsDfsPath = TRUE;

    }

    NtClose( hDfs );

    return( fIsDfsPath );

}

//+-------------------------------------------------------------------------
//
//  Function:   DfsOpen, private
//
//  Synopsis:   Opens a handle to the Dfs driver for fsctl purposes.
//
//  Arguments:  [DfsHandle] -- On successful return, contains handle to the
//                      driver.
//
//  Returns:    NTSTATUS of attempt to open the Dfs driver.
//
//--------------------------------------------------------------------------

NTSTATUS
DfsOpen(
    IN  OUT PHANDLE DfsHandle)
{
    NTSTATUS status;
    OBJECT_ATTRIBUTES objectAttributes;
    IO_STATUS_BLOCK ioStatus;
    UNICODE_STRING name = {
        sizeof(DFS_DRIVER_NAME)-sizeof(UNICODE_NULL),
        sizeof(DFS_DRIVER_NAME)-sizeof(UNICODE_NULL),
        DFS_DRIVER_NAME};

    InitializeObjectAttributes(
        &objectAttributes,
        &name,
        OBJ_CASE_INSENSITIVE,
        NULL,
        NULL
    );

    status = NtCreateFile(
        DfsHandle,
        SYNCHRONIZE,
        &objectAttributes,
        &ioStatus,
        NULL,
        FILE_ATTRIBUTE_NORMAL,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        FILE_OPEN_IF,
        FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
        NULL,
        0);

    if (NT_SUCCESS(status))
        status = ioStatus.Status;

    return status;
}


//+-------------------------------------------------------------------------
//
//  Function:   DfsFsctl, public
//
//  Synopsis:   Fsctl's to the Dfs driver.
//
//  Arguments:  [DfsHandle] -- Handle to the Dfs driver, usually obtained by
//                      calling DfsOpen.
//              [FsControlCode] -- The FSCTL code (see private\inc\dfsfsctl.h)
//              [InputBuffer] -- InputBuffer to the fsctl.
//              [InputBufferLength] -- Length, in BYTES, of InputBuffer
//              [OutputBuffer] -- OutputBuffer to the fsctl.
//              [OutputBufferLength] -- Length, in BYTES, of OutputBuffer
//
//  Returns:    NTSTATUS of Fsctl attempt.
//
//--------------------------------------------------------------------------

NTSTATUS
DfsFsctl(
    IN  HANDLE DfsHandle,
    IN  ULONG FsControlCode,
    IN  PVOID InputBuffer OPTIONAL,
    IN  ULONG InputBufferLength,
    OUT PVOID OutputBuffer OPTIONAL,
    IN  ULONG OutputBufferLength
)
{
    NTSTATUS status;
    IO_STATUS_BLOCK ioStatus;

    status = NtFsControlFile(
        DfsHandle,
        NULL,       // Event,
        NULL,       // ApcRoutine,
        NULL,       // ApcContext,
        &ioStatus,
        FsControlCode,
        InputBuffer,
        InputBufferLength,
        OutputBuffer,
        OutputBufferLength
    );

    if(NT_SUCCESS(status))
        status = ioStatus.Status;

    return status;
}


