/*++

Copyright (c) 1997  Microsoft Corporation

Module Name:

    volmount.c

Abstract:

    This file contains the implementation for the Volume Mount Point API.

Author:

    Norbert P. Kusters (norbertk) 22-Dec-1997

Revision History:

--*/

#include "basedll.h"
#include "initguid.h"
#include "mountmgr.h"

// NOTE, this structure is here because it was not defined in NTIOAPI.H.
// This should be taken out in the future.
// This is stolen from NTFS.H

typedef struct _REPARSE_INDEX_KEY {

    //
    //  The tag of the reparse point.
    //

    ULONG FileReparseTag;

    //
    //  The file record Id where the reparse point is set.
    //

    LARGE_INTEGER FileId;

} REPARSE_INDEX_KEY, *PREPARSE_INDEX_KEY;

HANDLE
WINAPI
FindFirstVolumeA(
    LPSTR lpszVolumeName,
    DWORD cchBufferLength
    )

{
    ANSI_STRING     ansiVolumeName;
    UNICODE_STRING  unicodeVolumeName;
    HANDLE          h;
    NTSTATUS        status;

    ansiVolumeName.Buffer = lpszVolumeName;
    ansiVolumeName.MaximumLength = (USHORT) (cchBufferLength - 1);
    unicodeVolumeName.Buffer = NULL;
    unicodeVolumeName.MaximumLength = 0;

    try {

        unicodeVolumeName.MaximumLength = (ansiVolumeName.MaximumLength + 1)*
                                          sizeof(WCHAR);
        unicodeVolumeName.Buffer =
                RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                unicodeVolumeName.MaximumLength);
        if (!unicodeVolumeName.Buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return INVALID_HANDLE_VALUE;
        }

        h = FindFirstVolumeW(unicodeVolumeName.Buffer, cchBufferLength);

        if (h != INVALID_HANDLE_VALUE) {

            RtlInitUnicodeString(&unicodeVolumeName, unicodeVolumeName.Buffer);

            status = BasepUnicodeStringTo8BitString(&ansiVolumeName,
                                                    &unicodeVolumeName, FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return INVALID_HANDLE_VALUE;
            }

            ansiVolumeName.Buffer[ansiVolumeName.Length] = 0;
        }

    } finally {

        if (unicodeVolumeName.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeName.Buffer);
        }
    }

    return h;
}

HANDLE
WINAPI
FindFirstVolumeW(
    LPWSTR lpszVolumeName,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine kicks off the enumeration of all volumes in the system.

Arguments:

    lpszVolumeName  - Returns the first volume name in the system.

    cchBufferLength - Supplies the size of the preceeding buffer.

Return Value:

    A valid handle or INVALID_HANDLE_VALUE.

--*/

{
    HANDLE                  h;
    MOUNTMGR_MOUNT_POINT    point;
    PMOUNTMGR_MOUNT_POINTS  points;
    BOOL                    b;
    DWORD                   bytes;

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                    INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        return INVALID_HANDLE_VALUE;
    }

    RtlZeroMemory(&point, sizeof(point));

    points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                             sizeof(MOUNTMGR_MOUNT_POINTS));
    if (!points) {
        CloseHandle(h);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return INVALID_HANDLE_VALUE;
    }

    b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, &point, sizeof(point),
                        points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL);
    while (!b && GetLastError() == ERROR_MORE_DATA) {
        bytes = points->Size;
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), bytes);
        if (!points) {
            CloseHandle(h);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return INVALID_HANDLE_VALUE;
        }

        b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, &point,
                            sizeof(point), points, bytes, &bytes, NULL);
    }

    CloseHandle(h);

    if (!b) {
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        return INVALID_HANDLE_VALUE;
    }

    b = FindNextVolumeW((HANDLE) points, lpszVolumeName, cchBufferLength);
    if (!b) {
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        return INVALID_HANDLE_VALUE;
    }

    return (HANDLE) points;
}

BOOL
WINAPI
FindNextVolumeA(
    HANDLE hFindVolume,
    LPSTR lpszVolumeName,
    DWORD cchBufferLength
    )

{
    ANSI_STRING     ansiVolumeName;
    UNICODE_STRING  unicodeVolumeName;
    BOOL            b;
    NTSTATUS        status;

    ansiVolumeName.Buffer = lpszVolumeName;
    ansiVolumeName.MaximumLength = (USHORT) (cchBufferLength - 1);
    unicodeVolumeName.Buffer = NULL;
    unicodeVolumeName.MaximumLength = 0;

    try {

        unicodeVolumeName.MaximumLength = (ansiVolumeName.MaximumLength + 1)*
                                          sizeof(WCHAR);
        unicodeVolumeName.Buffer =
                RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                unicodeVolumeName.MaximumLength);
        if (!unicodeVolumeName.Buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        b = FindNextVolumeW(hFindVolume, unicodeVolumeName.Buffer,
                            cchBufferLength);

        if (b) {

            RtlInitUnicodeString(&unicodeVolumeName, unicodeVolumeName.Buffer);

            status = BasepUnicodeStringTo8BitString(&ansiVolumeName,
                                                    &unicodeVolumeName, FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return FALSE;
            }

            ansiVolumeName.Buffer[ansiVolumeName.Length] = 0;
        }

    } finally {

        if (unicodeVolumeName.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeName.Buffer);
        }
    }

    return b;
}

BOOL
WINAPI
FindNextVolumeW(
    HANDLE hFindVolume,
    LPWSTR lpszVolumeName,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine continues the enumeration of all volumes in the system.

Arguments:

    hFindVolume     - Supplies the find volume handle.

    lpszVolumeName  - Returns the first volume name in the system.

    cchBufferLength - Supplies the size of the preceeding buffer.

Return Value:

    FALSE   - Failure.  The error code is returned in GetLastError().

    TRUE    - Success.

--*/

{
    PMOUNTMGR_MOUNT_POINTS  points = hFindVolume;
    DWORD                   i, j;
    PMOUNTMGR_MOUNT_POINT   point, point2;
    UNICODE_STRING          symName, symName2, devName, devName2;

    for (i = 0; i < points->NumberOfMountPoints; i++) {

        point = &points->MountPoints[i];
        if (!point->SymbolicLinkNameOffset) {
            continue;
        }

        symName.Length = symName.MaximumLength = point->SymbolicLinkNameLength;
        symName.Buffer = (PWSTR) ((PCHAR) points +
                                  point->SymbolicLinkNameOffset);

        if (!MOUNTMGR_IS_NT_VOLUME_NAME(&symName)) {
            point->SymbolicLinkNameOffset = 0;
            continue;
        }

        devName.Length = devName.MaximumLength = point->DeviceNameLength;
        devName.Buffer = (PWSTR) ((PCHAR) points +
                                  point->DeviceNameOffset);

        for (j = i + 1; j < points->NumberOfMountPoints; j++) {

            point2 = &points->MountPoints[j];
            if (!point2->SymbolicLinkNameOffset) {
                continue;
            }

            symName2.Length = symName2.MaximumLength =
                    point2->SymbolicLinkNameLength;
            symName2.Buffer = (PWSTR) ((PCHAR) points +
                                       point2->SymbolicLinkNameOffset);

            if (!MOUNTMGR_IS_NT_VOLUME_NAME(&symName2)) {
                point2->SymbolicLinkNameOffset = 0;
                continue;
            }

            devName2.Length = devName2.MaximumLength =
                    point2->DeviceNameLength;
            devName2.Buffer = (PWSTR) ((PCHAR) points +
                                       point2->DeviceNameOffset);

            if (RtlEqualUnicodeString(&devName, &devName2, TRUE)) {
                point2->SymbolicLinkNameOffset = 0;
            }
        }

        break;
    }

    if (i == points->NumberOfMountPoints) {
        SetLastError(ERROR_NO_MORE_FILES);
        return FALSE;
    }

    if (cchBufferLength*sizeof(WCHAR) < point->SymbolicLinkNameLength +
        2*sizeof(WCHAR)) {

        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        return FALSE;
    }

    RtlCopyMemory(lpszVolumeName, (PCHAR) points +
                  point->SymbolicLinkNameOffset,
                  point->SymbolicLinkNameLength);
    lpszVolumeName[1] = '\\';
    lpszVolumeName[point->SymbolicLinkNameLength/sizeof(WCHAR)] = '\\';
    lpszVolumeName[point->SymbolicLinkNameLength/sizeof(WCHAR) + 1] = 0;

    point->SymbolicLinkNameOffset = 0;

    return TRUE;
}

BOOL
WINAPI
FindVolumeClose(
    HANDLE hFindVolume
    )

{
    RtlFreeHeap(RtlProcessHeap(), 0, hFindVolume);
    return TRUE;
}

HANDLE
WINAPI
FindFirstVolumeMountPointA(
    LPCSTR lpszRootPathName,
    LPSTR lpszVolumeMountPoint,
    DWORD cchBufferLength
    )

{
    PUNICODE_STRING unicodeRootPathName;
    ANSI_STRING     ansiVolumeMountPoint;
    UNICODE_STRING  unicodeVolumeMountPoint;
    HANDLE          h;
    NTSTATUS        status;

    unicodeRootPathName =
            Basep8BitStringToStaticUnicodeString(lpszRootPathName);
    if (!unicodeRootPathName) {
        return INVALID_HANDLE_VALUE;
    }

    ansiVolumeMountPoint.Buffer = lpszVolumeMountPoint;
    ansiVolumeMountPoint.MaximumLength = (USHORT) (cchBufferLength - 1);
    unicodeVolumeMountPoint.Buffer = NULL;
    unicodeVolumeMountPoint.MaximumLength = 0;

    try {

        unicodeVolumeMountPoint.MaximumLength =
                (ansiVolumeMountPoint.MaximumLength + 1)*sizeof(WCHAR);
        unicodeVolumeMountPoint.Buffer =
                RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                unicodeVolumeMountPoint.MaximumLength);
        if (!unicodeVolumeMountPoint.Buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return INVALID_HANDLE_VALUE;
        }

        h = FindFirstVolumeMountPointW(unicodeRootPathName->Buffer,
                                       unicodeVolumeMountPoint.Buffer,
                                       cchBufferLength);

        if (h != INVALID_HANDLE_VALUE) {

            RtlInitUnicodeString(&unicodeVolumeMountPoint,
                                 unicodeVolumeMountPoint.Buffer);

            status = BasepUnicodeStringTo8BitString(&ansiVolumeMountPoint,
                                                    &unicodeVolumeMountPoint,
                                                    FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return INVALID_HANDLE_VALUE;
            }

            ansiVolumeMountPoint.Buffer[ansiVolumeMountPoint.Length] = 0;
        }

    } finally {

        if (unicodeVolumeMountPoint.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeMountPoint.Buffer);
        }
    }

    return h;
}

BOOL
FindNextVolumeMountPointHelper(
    HANDLE hFindVolumeMountPoint,
    LPWSTR lpszVolumeMountPoint,
    DWORD cchBufferLength,
    BOOL FirstTimeCalled
    )

/*++

Routine Description:

    This routine continues the enumeration of all volume mount point on the
    given volume.

Arguments:

    hFindVolumeMountPoint   - Supplies the handle for the enumeration.

    lpszVolumeMountPoint    - Returns the volume mount point.

    cchBufferLength         - Supplies the volume mount point buffer length.

    FirstTimeCalled         - Supplies whether or not this is being called
                                from FindFirst or from FindNext.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    REPARSE_INDEX_KEY                   reparseKey;
    UNICODE_STRING                      reparseName;
    NTSTATUS                            status;
    IO_STATUS_BLOCK                     ioStatus;
    FILE_REPARSE_POINT_INFORMATION      reparseInfo;
    UNICODE_STRING                      fileId;
    OBJECT_ATTRIBUTES                   oa;
    HANDLE                              h;
    PREPARSE_DATA_BUFFER                reparse;
    BOOL                                b;
    DWORD                               bytes;
    UNICODE_STRING                      mountName;
    DWORD                               nameInfoSize;
    PFILE_NAME_INFORMATION              nameInfo;

    for (;;) {

        if (FirstTimeCalled) {
            FirstTimeCalled = FALSE;
            RtlZeroMemory(&reparseKey, sizeof(reparseKey));
            reparseKey.FileReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
            reparseName.Length = reparseName.MaximumLength = sizeof(reparseKey);
            reparseName.Buffer = (PWCHAR) &reparseKey;
            status = NtQueryDirectoryFile(hFindVolumeMountPoint,
                                          NULL, NULL, NULL, &ioStatus,
                                          &reparseInfo, sizeof(reparseInfo),
                                          FileReparsePointInformation, TRUE,
                                          &reparseName, FALSE);
        } else {
            status = NtQueryDirectoryFile(hFindVolumeMountPoint,
                                          NULL, NULL, NULL, &ioStatus,
                                          &reparseInfo, sizeof(reparseInfo),
                                          FileReparsePointInformation, TRUE,
                                          NULL, FALSE);
        }

        if (!NT_SUCCESS(status)) {
            BaseSetLastNTError(status);
            return FALSE;
        }

        if (reparseInfo.Tag != IO_REPARSE_TAG_MOUNT_POINT) {
            SetLastError(ERROR_NO_MORE_FILES);
            return FALSE;
        }

        fileId.Length = sizeof(reparseInfo.FileReference);
        fileId.MaximumLength = fileId.Length;
        fileId.Buffer = (PWSTR) &reparseInfo.FileReference;

        InitializeObjectAttributes(&oa, &fileId, 0, hFindVolumeMountPoint,
                                   NULL);

        status = NtOpenFile(&h, FILE_GENERIC_READ, &oa, &ioStatus,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            FILE_OPEN_BY_FILE_ID | FILE_OPEN_REPARSE_POINT);
        if (!NT_SUCCESS(status)) {
            BaseSetLastNTError(status);
            return FALSE;
        }

        reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                  MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
        if (!reparse) {
            CloseHandle(h);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        b = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse,
                            MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes, NULL);

        if (!b || reparse->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
            RtlFreeHeap(RtlProcessHeap(), 0, reparse);
            CloseHandle(h);
            return FALSE;
        }

        mountName.Length = mountName.MaximumLength =
                reparse->MountPointReparseBuffer.SubstituteNameLength;
        mountName.Buffer = (PWSTR)
                ((PCHAR) reparse->MountPointReparseBuffer.PathBuffer +
                 reparse->MountPointReparseBuffer.SubstituteNameOffset);

        if (!MOUNTMGR_IS_NT_VOLUME_NAME_WB(&mountName)) {
            RtlFreeHeap(RtlProcessHeap(), 0, reparse);
            CloseHandle(h);
            continue;
        }

        RtlFreeHeap(RtlProcessHeap(), 0, reparse);

        nameInfoSize = FIELD_OFFSET(FILE_NAME_INFORMATION, FileName) +
                       (cchBufferLength - 1)*sizeof(WCHAR);
        nameInfo = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                   nameInfoSize);
        if (!nameInfo) {
            CloseHandle(h);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        status = NtQueryInformationFile(h, &ioStatus, nameInfo, nameInfoSize,
                                        FileNameInformation);
        if (!NT_SUCCESS(status)) {
            RtlFreeHeap(RtlProcessHeap(), 0, nameInfo);
            CloseHandle(h);
            BaseSetLastNTError(status);
            return FALSE;
        }

        RtlCopyMemory(lpszVolumeMountPoint, &nameInfo->FileName[1],
                      nameInfo->FileNameLength - sizeof(WCHAR));
        lpszVolumeMountPoint[nameInfo->FileNameLength/sizeof(WCHAR) - 1] = '\\';
        lpszVolumeMountPoint[nameInfo->FileNameLength/sizeof(WCHAR)] = 0;

        RtlFreeHeap(RtlProcessHeap(), 0, nameInfo);
        CloseHandle(h);
        break;
    }

    return TRUE;
}

HANDLE
WINAPI
FindFirstVolumeMountPointW(
    LPCWSTR lpszRootPathName,
    LPWSTR lpszVolumeMountPoint,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine kicks off the enumeration of all volume mount point on the
    given volume.

Arguments:

    lpszRootPathName        - Supplies the root path name.

    lpszVolumeMountPoint    - Returns the volume mount point.

    cchBufferLength         - Supplies the volume mount point buffer length.

Return Value:

    A handle or INVALID_HANDLE_VALUE.

--*/

{
    UNICODE_STRING                  unicodeRootPathName;
    UNICODE_STRING                  reparseSuffix, reparseName;
    HANDLE                          h;
    BOOL                            b;

    RtlInitUnicodeString(&unicodeRootPathName, lpszRootPathName);
    if (unicodeRootPathName.Buffer[
        unicodeRootPathName.Length/sizeof(WCHAR) - 1] != '\\') {

        BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
        return INVALID_HANDLE_VALUE;
    }

    RtlInitUnicodeString(&reparseSuffix,
                         L"$Extend\\$Reparse:$R:$INDEX_ALLOCATION");

    reparseName.MaximumLength = unicodeRootPathName.Length +
                                reparseSuffix.Length + sizeof(WCHAR);
    reparseName.Length = 0;
    reparseName.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                         reparseName.MaximumLength);
    if (!reparseName.Buffer) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return INVALID_HANDLE_VALUE;
    }

    RtlCopyUnicodeString(&reparseName, &unicodeRootPathName);
    RtlAppendUnicodeStringToString(&reparseName, &reparseSuffix);
    reparseName.Buffer[reparseName.Length/sizeof(WCHAR)] = 0;

    h = CreateFileW(reparseName.Buffer, GENERIC_READ, FILE_SHARE_READ, NULL,
                    OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS |
                    SECURITY_IMPERSONATION, NULL);

    RtlFreeHeap(RtlProcessHeap(), 0, reparseName.Buffer);

    if (h == INVALID_HANDLE_VALUE) {
        return INVALID_HANDLE_VALUE;
    }

    b = FindNextVolumeMountPointHelper(h, lpszVolumeMountPoint,
                                       cchBufferLength, TRUE);
    if (!b) {
        CloseHandle(h);
        return INVALID_HANDLE_VALUE;
    }

    return h;
}

BOOL
WINAPI
FindNextVolumeMountPointA(
    HANDLE hFindVolumeMountPoint,
    LPSTR lpszVolumeMountPoint,
    DWORD cchBufferLength
    )

{
    ANSI_STRING     ansiVolumeMountPoint;
    UNICODE_STRING  unicodeVolumeMountPoint;
    BOOL            b;
    NTSTATUS        status;

    ansiVolumeMountPoint.Buffer = lpszVolumeMountPoint;
    ansiVolumeMountPoint.MaximumLength = (USHORT) (cchBufferLength - 1);
    unicodeVolumeMountPoint.Buffer = NULL;
    unicodeVolumeMountPoint.MaximumLength = 0;

    try {

        unicodeVolumeMountPoint.MaximumLength =
                (ansiVolumeMountPoint.MaximumLength + 1)*sizeof(WCHAR);
        unicodeVolumeMountPoint.Buffer =
                RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                unicodeVolumeMountPoint.MaximumLength);
        if (!unicodeVolumeMountPoint.Buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        b = FindNextVolumeMountPointW(hFindVolumeMountPoint,
                                      unicodeVolumeMountPoint.Buffer,
                                      cchBufferLength);

        if (b) {

            RtlInitUnicodeString(&unicodeVolumeMountPoint,
                                 unicodeVolumeMountPoint.Buffer);

            status = BasepUnicodeStringTo8BitString(&ansiVolumeMountPoint,
                                                    &unicodeVolumeMountPoint,
                                                    FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return FALSE;
            }

            ansiVolumeMountPoint.Buffer[ansiVolumeMountPoint.Length] = 0;
        }

    } finally {

        if (unicodeVolumeMountPoint.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeMountPoint.Buffer);
        }
    }

    return b;
}

BOOL
IsThisAVolumeName(
    LPCWSTR     Name,
    PBOOLEAN    IsVolume
    )

/*++

Routine Description:

    This routine takes the given NT name and determines whether or not
    the name points to a volume.

Arguments:

    Name        - Supplies the name.

    IsVolume    - Returns whether or not the given name is a volume.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    UNICODE_STRING          name;
    PMOUNTMGR_MOUNT_POINT   point;
    MOUNTMGR_MOUNT_POINTS   points;
    HANDLE                  h;
    BOOL                    b;
    DWORD                   bytes;

    RtlInitUnicodeString(&name, Name);
    if (name.Buffer[name.Length/sizeof(WCHAR) - 1] == '\\') {
        name.Length -= sizeof(WCHAR);
    }
    point = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                            name.Length + sizeof(MOUNTMGR_MOUNT_POINT));
    if (!point) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    RtlZeroMemory(point, sizeof(MOUNTMGR_MOUNT_POINT));
    point->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
    point->DeviceNameLength = name.Length;
    RtlCopyMemory((PCHAR) point + point->DeviceNameOffset, name.Buffer,
                  point->DeviceNameLength);

    if (name.Length >= 4 && name.Buffer[1] == '\\') {
        ((PWSTR) ((PCHAR) point + point->DeviceNameOffset))[1] = '?';
    }

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ |
                    FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        RtlFreeHeap(RtlProcessHeap(), 0, point);
        return FALSE;
    }

    b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point,
                        name.Length + sizeof(MOUNTMGR_MOUNT_POINT),
                        &points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL);
    if (b) {
        if (points.NumberOfMountPoints) {
            *IsVolume = TRUE;
        } else {
            *IsVolume = FALSE;
        }
    } else {
        if (GetLastError() == ERROR_MORE_DATA) {
            *IsVolume = TRUE;
        } else {
            *IsVolume = FALSE;
        }
    }

    CloseHandle(h);
    RtlFreeHeap(RtlProcessHeap(), 0, point);

    return TRUE;
}

BOOL
WINAPI
FindNextVolumeMountPointW(
    HANDLE hFindVolumeMountPoint,
    LPWSTR lpszVolumeMountPoint,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine continues the enumeration of all volume mount point on the
    given volume.

Arguments:

    hFindVolumeMountPoint   - Supplies the handle for the enumeration.

    lpszVolumeMountPoint    - Returns the volume mount point.

    cchBufferLength         - Supplies the volume mount point buffer length.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    return FindNextVolumeMountPointHelper(hFindVolumeMountPoint,
                                          lpszVolumeMountPoint,
                                          cchBufferLength, FALSE);
}

BOOL
WINAPI
FindVolumeMountPointClose(
    HANDLE hFindVolumeMountPoint
    )

{
    return CloseHandle(hFindVolumeMountPoint);
}

BOOL
WINAPI
GetVolumeNameForVolumeMountPointA(
    LPCSTR lpszVolumeMountPoint,
    LPSTR lpszVolumeName,
    DWORD cchBufferLength
    )

{
    PUNICODE_STRING unicodeVolumeMountPoint;
    ANSI_STRING     ansiVolumeName;
    UNICODE_STRING  unicodeVolumeName;
    BOOL            b;
    NTSTATUS        status;

    unicodeVolumeMountPoint =
            Basep8BitStringToStaticUnicodeString(lpszVolumeMountPoint);
    if (!unicodeVolumeMountPoint) {
        return FALSE;
    }

    ansiVolumeName.Buffer = lpszVolumeName;
    ansiVolumeName.MaximumLength = (USHORT) (cchBufferLength - 1);
    unicodeVolumeName.Buffer = NULL;
    unicodeVolumeName.MaximumLength = 0;

    try {

        unicodeVolumeName.MaximumLength =
                (ansiVolumeName.MaximumLength + 1)*sizeof(WCHAR);
        unicodeVolumeName.Buffer =
                RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                unicodeVolumeName.MaximumLength);
        if (!unicodeVolumeName.Buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        b = GetVolumeNameForVolumeMountPointW(unicodeVolumeMountPoint->Buffer,
                                              unicodeVolumeName.Buffer,
                                              cchBufferLength);

        if (b) {

            RtlInitUnicodeString(&unicodeVolumeName,
                                 unicodeVolumeName.Buffer);

            status = BasepUnicodeStringTo8BitString(&ansiVolumeName,
                                                    &unicodeVolumeName,
                                                    FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return FALSE;
            }

            ansiVolumeName.Buffer[ansiVolumeName.Length] = 0;
        }

    } finally {

        if (unicodeVolumeName.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumeName.Buffer);
        }
    }

    return b;
}

BOOL
GetVolumeNameForRoot(
    LPCWSTR DeviceName,
    LPWSTR lpszVolumeName,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine queries the volume name for the given NT device name.

Arguments:

    DeviceName      - Supplies a DOS device name terminated by a '\'.

    lpszVolumeName  - Returns the volume name pointed to by the DOS
                        device name.

    cchBufferLength - Supplies the size of the preceeding buffer.

Return Value:

    FALSE   - Failure.  The error code is returned in GetLastError().

    TRUE    - Success.

--*/

{
    NTSTATUS                status;
    UNICODE_STRING          devicePath, symName;
    OBJECT_ATTRIBUTES       oa;
    HANDLE                  h;
    IO_STATUS_BLOCK         ioStatus;
    WCHAR                   buffer[MAX_PATH];
    PMOUNTDEV_NAME          name;
    BOOL                    b;
    DWORD                   bytes, i;
    PMOUNTMGR_MOUNT_POINT   point;
    PMOUNTMGR_MOUNT_POINTS  points;

    if (GetDriveTypeW(DeviceName) == DRIVE_REMOTE) {
        SetLastError(ERROR_PATH_NOT_FOUND);
        return FALSE;
    }

    if (!RtlDosPathNameToNtPathName_U(DeviceName, &devicePath, NULL, NULL)) {
        SetLastError(ERROR_PATH_NOT_FOUND);
        return FALSE;
    }

    if (devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 1] == '\\') {
        devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 1] = 0;
        devicePath.Length -= sizeof(WCHAR);
    }

    if (devicePath.Length >= 2*sizeof(WCHAR) &&
        devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 1] == ':') {

        devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 2] = (WCHAR)
            toupper(devicePath.Buffer[devicePath.Length/sizeof(WCHAR) - 2]);
    }

    InitializeObjectAttributes(&oa, &devicePath, OBJ_CASE_INSENSITIVE,
                               NULL, NULL);

    status = NtOpenFile(&h, FILE_READ_ATTRIBUTES | SYNCHRONIZE, &oa,
                        &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE,
                        FILE_SYNCHRONOUS_IO_ALERT);
    if (!NT_SUCCESS(status)) {
        RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);
        SetLastError(RtlNtStatusToDosError(status));
        return FALSE;
    }

    name = (PMOUNTDEV_NAME) buffer;
    b = DeviceIoControl(h, IOCTL_MOUNTDEV_QUERY_DEVICE_NAME, NULL, 0, name,
                        MAX_PATH*sizeof(WCHAR), &bytes, NULL);
    NtClose(h);

    if (!b) {
        RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);
        return FALSE;
    }

    RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);

    devicePath.Length = name->NameLength;
    devicePath.MaximumLength = devicePath.Length + sizeof(WCHAR);

    devicePath.Buffer = RtlAllocateHeap(RtlProcessHeap(),
                                        MAKE_TAG(TMP_TAG),
                                        devicePath.MaximumLength);
    if (!devicePath.Buffer) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    RtlCopyMemory(devicePath.Buffer, name->Name, name->NameLength);
    devicePath.Buffer[devicePath.Length/sizeof(WCHAR)] = 0;

    point = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                            devicePath.Length + sizeof(MOUNTMGR_MOUNT_POINT));
    if (!point) {
        RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    RtlZeroMemory(point, sizeof(MOUNTMGR_MOUNT_POINT));
    point->DeviceNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
    point->DeviceNameLength = devicePath.Length;
    RtlCopyMemory((PCHAR) point + point->DeviceNameOffset,
                  devicePath.Buffer, point->DeviceNameLength);

    RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);

    points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                             sizeof(MOUNTMGR_MOUNT_POINTS));
    if (!points) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        RtlFreeHeap(RtlProcessHeap(), 0, point);
        return FALSE;
    }

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
                    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
                    INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        RtlFreeHeap(RtlProcessHeap(), 0, point);
        return FALSE;
    }

    b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point,
                        devicePath.Length + sizeof(MOUNTMGR_MOUNT_POINT),
                        points, sizeof(MOUNTMGR_MOUNT_POINTS), &bytes, NULL);
    while (!b && GetLastError() == ERROR_MORE_DATA) {
        bytes = points->Size;
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        points = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), bytes);
        if (!points) {
            CloseHandle(h);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            RtlFreeHeap(RtlProcessHeap(), 0, point);
            return FALSE;
        }

        b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_POINTS, point,
                            devicePath.Length + sizeof(MOUNTMGR_MOUNT_POINT),
                            points, bytes, &bytes, NULL);
    }

    CloseHandle(h);
    RtlFreeHeap(RtlProcessHeap(), 0, point);

    if (!b) {
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    for (i = 0; i < points->NumberOfMountPoints; i++) {

        symName.Length = symName.MaximumLength =
                points->MountPoints[i].SymbolicLinkNameLength;
        symName.Buffer = (PWSTR) ((PCHAR) points +
                         points->MountPoints[i].SymbolicLinkNameOffset);

        if (MOUNTMGR_IS_NT_VOLUME_NAME(&symName)) {
            break;
        }
    }

    if (i == points->NumberOfMountPoints) {
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if (cchBufferLength*sizeof(WCHAR) < symName.Length + 2*sizeof(WCHAR)) {
        RtlFreeHeap(RtlProcessHeap(), 0, points);
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        return FALSE;
    }

    RtlCopyMemory(lpszVolumeName, symName.Buffer, symName.Length);
    lpszVolumeName[1] = '\\';
    lpszVolumeName[symName.Length/sizeof(WCHAR)] = '\\';
    lpszVolumeName[symName.Length/sizeof(WCHAR) + 1] = 0;

    RtlFreeHeap(RtlProcessHeap(), 0, points);

    return TRUE;
}

BOOL
BasepGetVolumeNameFromReparsePoint(
    LPCWSTR lpszVolumeMountPoint,
    LPWSTR lpszVolumeName,
    DWORD cchBufferLength,
    PBOOL ResultOfOpen
    )

{
    HANDLE                  h;
    PREPARSE_DATA_BUFFER    reparse;
    BOOL                    b;
    DWORD                   bytes;
    UNICODE_STRING          mountName;
    WCHAR                   c;

    h = CreateFileW(lpszVolumeMountPoint, 0, FILE_SHARE_READ |
                    FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT |
                    FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        if (ResultOfOpen) {
            *ResultOfOpen = FALSE;
        }
        return FALSE;
    }

    if (ResultOfOpen) {
        *ResultOfOpen = TRUE;
    }

    reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                              MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
    if (!reparse) {
        CloseHandle(h);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    b = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse,
                        MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes, NULL);
    CloseHandle(h);

    if (!b || reparse->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
        RtlFreeHeap(RtlProcessHeap(), 0, reparse);
        return FALSE;
    }

    if (cchBufferLength*sizeof(WCHAR) <
        reparse->MountPointReparseBuffer.SubstituteNameLength + sizeof(WCHAR)) {

        RtlFreeHeap(RtlProcessHeap(), 0, reparse);
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        return FALSE;
    }

    RtlCopyMemory(lpszVolumeName,
                  (PCHAR) reparse->MountPointReparseBuffer.PathBuffer +
                  reparse->MountPointReparseBuffer.SubstituteNameOffset,
                  reparse->MountPointReparseBuffer.SubstituteNameLength);

    c = lpszVolumeName[1];
    lpszVolumeName[1] = '\\';
    lpszVolumeName[reparse->MountPointReparseBuffer.SubstituteNameLength/
                   sizeof(WCHAR)] = 0;

    mountName.Length = mountName.MaximumLength =
            reparse->MountPointReparseBuffer.SubstituteNameLength;
    mountName.Buffer = lpszVolumeName;

    RtlFreeHeap(RtlProcessHeap(), 0, reparse);

    if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&mountName)) {
        lpszVolumeName[1] = c;
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    return TRUE;
}

BOOL
BasepGetVolumeNameForVolumeMountPoint(
    LPCWSTR lpszVolumeMountPoint,
    LPWSTR lpszVolumeName,
    DWORD cchBufferLength,
    PBOOL ResultOfOpen
    )

/*++

Routine Description:

    This routine returns the volume name for a given volume mount point.

Arguments:

    lpszVolumeMountPoint    - Supplies the volume mount point.

    lpszVolumeName          - Returns the volume name.

    cchBufferLength         - Supplies the size of the volume name buffer.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    UNICODE_STRING  unicodeVolumeMountPoint;
    BOOL            b;

    if (ResultOfOpen) {
        *ResultOfOpen = TRUE;
    }

    RtlInitUnicodeString(&unicodeVolumeMountPoint, lpszVolumeMountPoint);
    if (unicodeVolumeMountPoint.Buffer[
        unicodeVolumeMountPoint.Length/sizeof(WCHAR) - 1] != '\\') {

        BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
        if (lpszVolumeName && cchBufferLength >= 1) {
            *lpszVolumeName = 0;
        }
        return FALSE;
    }

    if (unicodeVolumeMountPoint.Length == 6 &&
        unicodeVolumeMountPoint.Buffer[1] == ':') {

        b = GetVolumeNameForRoot(lpszVolumeMountPoint, lpszVolumeName,
                                 cchBufferLength);
        if (!b && lpszVolumeName && cchBufferLength >= 1) {
            *lpszVolumeName = 0;
        }
        return b;
    }

    if (unicodeVolumeMountPoint.Length == 14 &&
        unicodeVolumeMountPoint.Buffer[0] == '\\' &&
        unicodeVolumeMountPoint.Buffer[1] == '\\' &&
        (unicodeVolumeMountPoint.Buffer[2] == '.' ||
         unicodeVolumeMountPoint.Buffer[2] == '?') &&
        unicodeVolumeMountPoint.Buffer[3] == '\\' &&
        unicodeVolumeMountPoint.Buffer[5] == ':') {

        b = GetVolumeNameForRoot(lpszVolumeMountPoint + 4,
                                 lpszVolumeName, cchBufferLength);
        if (!b && lpszVolumeName && cchBufferLength >= 1) {
            *lpszVolumeName = 0;
        }
        return b;
    }

    if (GetVolumeNameForRoot(lpszVolumeMountPoint, lpszVolumeName,
                             cchBufferLength)) {

        return TRUE;
    }

    b = BasepGetVolumeNameFromReparsePoint(lpszVolumeMountPoint,
                                           lpszVolumeName, cchBufferLength,
                                           ResultOfOpen);
    if (!b && lpszVolumeName && cchBufferLength >= 1) {
        *lpszVolumeName = 0;
    }

    return b;
}

BOOL
WINAPI
GetVolumeNameForVolumeMountPointW(
    LPCWSTR lpszVolumeMountPoint,
    LPWSTR lpszVolumeName,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine returns the volume name for a given volume mount point.

Arguments:

    lpszVolumeMountPoint    - Supplies the volume mount point.

    lpszVolumeName          - Returns the volume name.

    cchBufferLength         - Supplies the size of the volume name buffer.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    return BasepGetVolumeNameForVolumeMountPoint(lpszVolumeMountPoint,
                                                 lpszVolumeName,
                                                 cchBufferLength, NULL);
}

BOOL
WINAPI
SetVolumeMountPointA(
    LPCSTR lpszVolumeMountPoint,
    LPCSTR lpszVolumeName
    )

{
    PUNICODE_STRING unicodeVolumeMountPoint;
    UNICODE_STRING  unicodeVolumeName;
    BOOL            b;

    unicodeVolumeMountPoint = Basep8BitStringToStaticUnicodeString(
                              lpszVolumeMountPoint);
    if (!unicodeVolumeMountPoint) {
        return FALSE;
    }

    if (!Basep8BitStringToDynamicUnicodeString(&unicodeVolumeName,
                                               lpszVolumeName)) {
        return FALSE;
    }

    b = SetVolumeMountPointW(unicodeVolumeMountPoint->Buffer,
                             unicodeVolumeName.Buffer);

    RtlFreeUnicodeString(&unicodeVolumeName);

    return b;
}

BOOL
SetVolumeNameForRoot(
    LPCWSTR DeviceName,
    LPCWSTR lpszVolumeName
    )

/*++

Routine Description:

    This routine sets the volume name for the given DOS device name.

Arguments:

    DeviceName      - Supplies a DOS device name terminated by a '\'.

    lpszVolumeName  - Supplies the volume name that the DOS device name
                        will point to.

Return Value:

    FALSE   - Failure.  The error code is returned in GetLastError().

    TRUE    - Success.

--*/

{
    UNICODE_STRING                  devicePath, volumeName;
    DWORD                           inputLength;
    PMOUNTMGR_CREATE_POINT_INPUT    input;
    HANDLE                          h;
    BOOL                            b;
    DWORD                           bytes;

    devicePath.Length = 28;
    devicePath.MaximumLength = devicePath.Length + sizeof(WCHAR);
    devicePath.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                        devicePath.MaximumLength);
    if (!devicePath.Buffer) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }
    RtlCopyMemory(devicePath.Buffer, L"\\DosDevices\\", 24);

    devicePath.Buffer[12] = (WCHAR)toupper(DeviceName[0]);
    devicePath.Buffer[13] = ':';
    devicePath.Buffer[14] = 0;

    RtlInitUnicodeString(&volumeName, lpszVolumeName);
    volumeName.Length -= sizeof(WCHAR);

    inputLength = sizeof(MOUNTMGR_CREATE_POINT_INPUT) + devicePath.Length +
                  volumeName.Length;
    input = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), inputLength);
    if (!input) {
        RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_CREATE_POINT_INPUT);
    input->SymbolicLinkNameLength = devicePath.Length;
    input->DeviceNameOffset = input->SymbolicLinkNameOffset +
                              input->SymbolicLinkNameLength;
    input->DeviceNameLength = volumeName.Length;
    RtlCopyMemory((PCHAR) input + input->SymbolicLinkNameOffset,
                  devicePath.Buffer, input->SymbolicLinkNameLength);
    RtlCopyMemory((PCHAR) input + input->DeviceNameOffset, volumeName.Buffer,
                  input->DeviceNameLength);
    ((PWSTR) ((PCHAR) input + input->DeviceNameOffset))[1] = '?';

    RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        RtlFreeHeap(RtlProcessHeap(), 0, input);
        return FALSE;
    }

    b = DeviceIoControl(h, IOCTL_MOUNTMGR_CREATE_POINT, input, inputLength,
                        NULL, 0, &bytes, NULL);

    CloseHandle(h);
    RtlFreeHeap(RtlProcessHeap(), 0, input);

    return b;
}

VOID
NotifyMountMgr(
    LPCWSTR lpszVolumeMountPoint,
    LPCWSTR lpszVolumeName,
    BOOL IsPointCreated
    )

/*++

Routine Description:

    This routine notifies the mount mgr that a volume mount point was
    created or deleted so that the mount mgr can update the remote database on
    the volume where the mount point was created or deleted.

Arguments:

    lpszVolumeMountPoint    - Supplies the directory where the volume mount
                                point resides.

    lpszVolumeName          - Supplies the volume name.

    IsPointCreated          - Supplies wheter or not the point was created or
                                deleted.

Return Value:

    None.

--*/

{
    UNICODE_STRING                  unicodeSourceVolumeName;
    UNICODE_STRING                  unicodeTargetVolumeName;
    DWORD                           inputSize;
    PMOUNTMGR_VOLUME_MOUNT_POINT    input;
    HANDLE                          h;
    DWORD                           ioControl, bytes;

    if (!RtlDosPathNameToNtPathName_U(lpszVolumeMountPoint,
                                      &unicodeSourceVolumeName, NULL, NULL)) {

        return;
    }

    RtlInitUnicodeString(&unicodeTargetVolumeName, lpszVolumeName);
    unicodeSourceVolumeName.Length -= sizeof(WCHAR);
    unicodeTargetVolumeName.Length -= sizeof(WCHAR);

    inputSize = sizeof(MOUNTMGR_VOLUME_MOUNT_POINT) +
                unicodeSourceVolumeName.Length +
                unicodeTargetVolumeName.Length;
    input = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), inputSize);
    if (!input) {
        RtlFreeHeap(RtlProcessHeap(), 0, unicodeSourceVolumeName.Buffer);
        return;
    }

    input->SourceVolumeNameOffset = sizeof(MOUNTMGR_VOLUME_MOUNT_POINT);
    input->SourceVolumeNameLength = unicodeSourceVolumeName.Length;
    input->TargetVolumeNameOffset = input->SourceVolumeNameOffset +
                                    input->SourceVolumeNameLength;
    input->TargetVolumeNameLength = unicodeTargetVolumeName.Length;

    RtlCopyMemory((PCHAR) input + input->SourceVolumeNameOffset,
                  unicodeSourceVolumeName.Buffer,
                  input->SourceVolumeNameLength);

    RtlCopyMemory((PCHAR) input + input->TargetVolumeNameOffset,
                  unicodeTargetVolumeName.Buffer,
                  input->TargetVolumeNameLength);

    ((PWSTR) ((PCHAR) input + input->TargetVolumeNameOffset))[1] = '?';

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        RtlFreeHeap(RtlProcessHeap(), 0, input);
        RtlFreeHeap(RtlProcessHeap(), 0, unicodeSourceVolumeName.Buffer);
        return;
    }

    if (IsPointCreated) {
        ioControl = IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_CREATED;
    } else {
        ioControl = IOCTL_MOUNTMGR_VOLUME_MOUNT_POINT_DELETED;
    }

    DeviceIoControl(h, ioControl, input, inputSize, NULL, 0, &bytes, NULL);

    CloseHandle(h);
    RtlFreeHeap(RtlProcessHeap(), 0, input);
    RtlFreeHeap(RtlProcessHeap(), 0, unicodeSourceVolumeName.Buffer);
}

BOOL
WINAPI
SetVolumeMountPointW(
    LPCWSTR lpszVolumeMountPoint,
    LPCWSTR lpszVolumeName
    )

/*++

Routine Description:

    This routine sets a mount point on the given directory pointed to by
    'VolumeMountPoint' which points to the volume given by 'VolumeName'.
    In the case when 'VolumeMountPoint' is of the form "D:\", the drive
    letter for the given volume is set to 'D:'.

Arguments:

    lpszVolumeMountPoint    - Supplies the directory where the volume mount
                                point will reside.

    lpszVolumeName          - Supplies the volume name.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    UNICODE_STRING          unicodeVolumeMountPoint;
    UNICODE_STRING          unicodeVolumeName;
    BOOLEAN                 isVolume;
    BOOL                    b;
    WCHAR                   volumeMountPointVolumePrefix[MAX_PATH];
    HANDLE                  h;
    PREPARSE_DATA_BUFFER    reparse;
    DWORD                   bytes;

    if (GetVolumeNameForVolumeMountPointW(lpszVolumeMountPoint,
                                          volumeMountPointVolumePrefix,
                                          MAX_PATH) ||
        GetLastError() == ERROR_FILENAME_EXCED_RANGE) {

        SetLastError(ERROR_DIR_NOT_EMPTY);
        return FALSE;
    }

    RtlInitUnicodeString(&unicodeVolumeMountPoint, lpszVolumeMountPoint);
    RtlInitUnicodeString(&unicodeVolumeName, lpszVolumeName);

    if (unicodeVolumeMountPoint.Length == 0 ||
        unicodeVolumeName.Length == 0) {

        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if (unicodeVolumeMountPoint.Buffer[
        unicodeVolumeMountPoint.Length/sizeof(WCHAR) - 1] != '\\' ||
        unicodeVolumeName.Buffer[
        unicodeVolumeName.Length/sizeof(WCHAR) - 1] != '\\') {

        BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
        return FALSE;
    }

    if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&unicodeVolumeName)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if (!IsThisAVolumeName(lpszVolumeName, &isVolume)) {
        return FALSE;
    }
    if (!isVolume) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    if (unicodeVolumeMountPoint.Length == 6 &&
        unicodeVolumeMountPoint.Buffer[1] == ':') {

        return SetVolumeNameForRoot(lpszVolumeMountPoint, lpszVolumeName);
    }

    if (unicodeVolumeMountPoint.Length == 14 &&
        unicodeVolumeMountPoint.Buffer[0] == '\\' &&
        unicodeVolumeMountPoint.Buffer[1] == '\\' &&
        (unicodeVolumeMountPoint.Buffer[2] == '.' ||
         unicodeVolumeMountPoint.Buffer[2] == '?') &&
        unicodeVolumeMountPoint.Buffer[3] == '\\' &&
        unicodeVolumeMountPoint.Buffer[5] == ':') {

        return SetVolumeNameForRoot(lpszVolumeMountPoint + 4, lpszVolumeName);
    }

    if (GetDriveTypeW(lpszVolumeMountPoint) != DRIVE_FIXED) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    CloseHandle(h);

    h = CreateFileW(lpszVolumeMountPoint, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT |
                    FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                              MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
    if (!reparse) {
        CloseHandle(h);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    reparse->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
    reparse->ReparseDataLength = (USHORT) (FIELD_OFFSET(REPARSE_DATA_BUFFER,
                                              MountPointReparseBuffer.PathBuffer) -
                                 REPARSE_DATA_BUFFER_HEADER_SIZE +
                                 unicodeVolumeName.Length + 2*sizeof(WCHAR));
    reparse->Reserved = 0;
    reparse->MountPointReparseBuffer.SubstituteNameOffset = 0;
    reparse->MountPointReparseBuffer.SubstituteNameLength = unicodeVolumeName.Length;
    reparse->MountPointReparseBuffer.PrintNameOffset =
            reparse->MountPointReparseBuffer.SubstituteNameLength +
            sizeof(WCHAR);
    reparse->MountPointReparseBuffer.PrintNameLength = 0;

    CopyMemory(reparse->MountPointReparseBuffer.PathBuffer,
               unicodeVolumeName.Buffer,
               reparse->MountPointReparseBuffer.SubstituteNameLength);

    reparse->MountPointReparseBuffer.PathBuffer[1] = '?';
    reparse->MountPointReparseBuffer.PathBuffer[
            unicodeVolumeName.Length/sizeof(WCHAR)] = 0;
    reparse->MountPointReparseBuffer.PathBuffer[
            unicodeVolumeName.Length/sizeof(WCHAR) + 1] = 0;

    b = DeviceIoControl(h, FSCTL_SET_REPARSE_POINT, reparse,
                        REPARSE_DATA_BUFFER_HEADER_SIZE +
                        reparse->ReparseDataLength, NULL, 0, &bytes, NULL);

    RtlFreeHeap(RtlProcessHeap(), 0, reparse);
    CloseHandle(h);

    if (b) {
        NotifyMountMgr(lpszVolumeMountPoint, lpszVolumeName, TRUE);
    }

    return b;
}

BOOL
WINAPI
DeleteVolumeMountPointA(
    LPCSTR lpszVolumeMountPoint
    )

{
    PUNICODE_STRING unicodeVolumeMountPoint;
    BOOL            b;

    unicodeVolumeMountPoint = Basep8BitStringToStaticUnicodeString(
                              lpszVolumeMountPoint);
    if (!unicodeVolumeMountPoint) {
        return FALSE;
    }

    b = DeleteVolumeMountPointW(unicodeVolumeMountPoint->Buffer);

    return b;
}

BOOL
DeleteVolumeNameForRoot(
    LPCWSTR DeviceName
    )

/*++

Routine Description:

    This routine deletes the given DOS device name.

Arguments:

    DeviceName  - Supplies a DOS device name terminated by a '\'.

Return Value:

    FALSE   - Failure.  The error code is returned in GetLastError().

    TRUE    - Success.

--*/

{
    UNICODE_STRING          devicePath;
    DWORD                   inputLength;
    PMOUNTMGR_MOUNT_POINT   input;
    DWORD                   outputLength;
    PMOUNTMGR_MOUNT_POINTS  output;
    HANDLE                  h;
    BOOL                    b;
    DWORD                   bytes;

    devicePath.Length = 28;
    devicePath.MaximumLength = devicePath.Length + sizeof(WCHAR);
    devicePath.Buffer = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                        devicePath.MaximumLength);
    if (!devicePath.Buffer) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }
    RtlCopyMemory(devicePath.Buffer, L"\\DosDevices\\", 24);

    devicePath.Buffer[12] = (WCHAR)toupper(DeviceName[0]);
    devicePath.Buffer[13] = ':';
    devicePath.Buffer[14] = 0;

    inputLength = sizeof(MOUNTMGR_MOUNT_POINT) + devicePath.Length;
    input = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), inputLength);
    if (!input) {
        RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    RtlZeroMemory(input, sizeof(MOUNTMGR_MOUNT_POINT));
    input->SymbolicLinkNameOffset = sizeof(MOUNTMGR_MOUNT_POINT);
    input->SymbolicLinkNameLength = devicePath.Length;
    RtlCopyMemory((PCHAR) input + input->SymbolicLinkNameOffset,
                  devicePath.Buffer, input->SymbolicLinkNameLength);

    RtlFreeHeap(RtlProcessHeap(), 0, devicePath.Buffer);

    outputLength = sizeof(MOUNTMGR_MOUNT_POINTS) + 3*MAX_PATH*sizeof(WCHAR);
    output = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                             outputLength);
    if (!output) {
        RtlFreeHeap(RtlProcessHeap(), 0, input);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        RtlFreeHeap(RtlProcessHeap(), 0, output);
        RtlFreeHeap(RtlProcessHeap(), 0, input);
        return FALSE;
    }

    b = DeviceIoControl(h, IOCTL_MOUNTMGR_DELETE_POINTS, input, inputLength,
                        output, outputLength, &bytes, NULL);

    CloseHandle(h);
    RtlFreeHeap(RtlProcessHeap(), 0, output);
    RtlFreeHeap(RtlProcessHeap(), 0, input);

    return b;
}

BOOL
WINAPI
DeleteVolumeMountPointW(
    LPCWSTR lpszVolumeMountPoint
    )

/*++

Routine Description:

    This routine removes the NTFS junction point from the given directory
    or remove the drive letter symbolic link pointing to the given volume.

Arguments:

    lpszVolumeMountPoint    - Supplies the volume mount piont.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    UNICODE_STRING          unicodeVolumeMountPoint;
    HANDLE                  h;
    PREPARSE_DATA_BUFFER    reparse;
    BOOL                    b;
    DWORD                   bytes;
    UNICODE_STRING          substituteName;

    RtlInitUnicodeString(&unicodeVolumeMountPoint, lpszVolumeMountPoint);

    if (unicodeVolumeMountPoint.Buffer[
        unicodeVolumeMountPoint.Length/sizeof(WCHAR) - 1] != '\\') {

        BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
        return FALSE;
    }

    if (unicodeVolumeMountPoint.Length == 6 &&
        unicodeVolumeMountPoint.Buffer[1] == ':') {

        return DeleteVolumeNameForRoot(lpszVolumeMountPoint);
    }

    if (unicodeVolumeMountPoint.Length == 14 &&
        unicodeVolumeMountPoint.Buffer[0] == '\\' &&
        unicodeVolumeMountPoint.Buffer[1] == '\\' &&
        (unicodeVolumeMountPoint.Buffer[2] == '.' ||
         unicodeVolumeMountPoint.Buffer[2] == '?') &&
        unicodeVolumeMountPoint.Buffer[3] == '\\' &&
        unicodeVolumeMountPoint.Buffer[5] == ':') {

        return DeleteVolumeNameForRoot(lpszVolumeMountPoint + 4);
    }

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    CloseHandle(h);

    h = CreateFileW(lpszVolumeMountPoint, GENERIC_READ | GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
                    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL |
                    FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
                    INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        return FALSE;
    }

    reparse = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                              MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
    if (!reparse) {
        CloseHandle(h);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    b = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, reparse,
                        MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bytes, NULL);

    if (!b || reparse->ReparseTag != IO_REPARSE_TAG_MOUNT_POINT) {
        RtlFreeHeap(RtlProcessHeap(), 0, reparse);
        CloseHandle(h);
        return FALSE;
    }

    substituteName.MaximumLength = substituteName.Length =
            reparse->MountPointReparseBuffer.SubstituteNameLength;
    substituteName.Buffer = (PWSTR)
            ((PCHAR) reparse->MountPointReparseBuffer.PathBuffer +
             reparse->MountPointReparseBuffer.SubstituteNameOffset);

    if (!MOUNTMGR_IS_NT_VOLUME_NAME_WB(&substituteName)) {
        RtlFreeHeap(RtlProcessHeap(), 0, reparse);
        CloseHandle(h);
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    reparse->ReparseDataLength = 0;

    b = DeviceIoControl(h, FSCTL_DELETE_REPARSE_POINT, reparse,
                        REPARSE_DATA_BUFFER_HEADER_SIZE, NULL, 0, &bytes,
                        NULL);

    CloseHandle(h);

    if (b) {
        substituteName.Buffer[1] = '\\';
        substituteName.Buffer[substituteName.Length/sizeof(WCHAR)] = 0;
        NotifyMountMgr(lpszVolumeMountPoint, substituteName.Buffer,
                       FALSE);
    }

    RtlFreeHeap(RtlProcessHeap(), 0, reparse);

    return b;
}

BOOL
WINAPI
GetVolumePathNameA(
    LPCSTR lpszFileName,
    LPSTR lpszVolumePathName,
    DWORD cchBufferLength
    )

{
    PUNICODE_STRING unicodeFileName;
    ANSI_STRING     ansiVolumePathName;
    UNICODE_STRING  unicodeVolumePathName;
    BOOL            b;
    NTSTATUS        status;

    unicodeFileName = Basep8BitStringToStaticUnicodeString(lpszFileName);
    if (!unicodeFileName) {
        return FALSE;
    }

    ansiVolumePathName.Buffer = lpszVolumePathName;
    ansiVolumePathName.MaximumLength = (USHORT) (cchBufferLength - 1);
    unicodeVolumePathName.Buffer = NULL;
    unicodeVolumePathName.MaximumLength = 0;

    try {

        unicodeVolumePathName.MaximumLength =
                (ansiVolumePathName.MaximumLength + 1)*sizeof(WCHAR);
        unicodeVolumePathName.Buffer =
                RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                unicodeVolumePathName.MaximumLength);
        if (!unicodeVolumePathName.Buffer) {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }

        b = GetVolumePathNameW(unicodeFileName->Buffer,
                               unicodeVolumePathName.Buffer,
                               cchBufferLength);

        if (b) {

            RtlInitUnicodeString(&unicodeVolumePathName,
                                 unicodeVolumePathName.Buffer);

            status = BasepUnicodeStringTo8BitString(&ansiVolumePathName,
                                                    &unicodeVolumePathName,
                                                    FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return FALSE;
            }

            ansiVolumePathName.Buffer[ansiVolumePathName.Length] = 0;
        }

    } finally {

        if (unicodeVolumePathName.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumePathName.Buffer);
        }
    }

    return b;
}

BOOL
WINAPI
GetVolumePathNameW(
    LPCWSTR lpszFileName,
    LPWSTR lpszVolumePathName,
    DWORD cchBufferLength
    )

/*++

Routine Description:

    This routine will return a full path whose prefix is the longest prefix
    that represents a volume.

Arguments:

    lpszFileName        - Supplies the file name.

    lpszVolumePathName  - Returns the volume path name.

    cchBufferLength     - Supplies the volume path name buffer length.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    DWORD           fullPathLength;
    PWSTR           fullPath, p;
    WCHAR           c;
    UNICODE_STRING  name, dosName, prefix;
    BOOL            b, resultOfOpen;
    PWSTR           volumeName;
    DWORD           i;

    fullPathLength = GetFullPathNameW(lpszFileName, 0, NULL, NULL);
    if (!fullPathLength) {
        return FALSE;
    }
    fullPathLength += 10;

    fullPath = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                               fullPathLength*sizeof(WCHAR));
    if (!fullPath) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    if (!GetFullPathNameW(lpszFileName, fullPathLength, fullPath, &p)) {
        RtlFreeHeap(RtlProcessHeap(), 0, fullPath);
        return FALSE;
    }

    RtlInitUnicodeString(&name, fullPath);

    //
    // Append a trailing backslash to start the search.
    //

    if (name.Buffer[(name.Length/sizeof(WCHAR)) - 1] != '\\') {
        name.Length += sizeof(WCHAR);
        name.Buffer[(name.Length/sizeof(WCHAR)) - 1] = '\\';
        name.Buffer[name.Length/sizeof(WCHAR)] = 0;
    }

    volumeName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                 MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
    if (!volumeName) {
        RtlFreeHeap(RtlProcessHeap(), 0, fullPath);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    p = NULL;
    c = 0;

    for (;;) {

        b = BasepGetVolumeNameForVolumeMountPoint(
                name.Buffer, volumeName, MAXIMUM_REPARSE_DATA_BUFFER_SIZE/
                sizeof(WCHAR), &resultOfOpen);
        if (b) {
            break;
        }

        if (!resultOfOpen && GetLastError() == ERROR_ACCESS_DENIED) {
            resultOfOpen = TRUE;
        }

        if (*volumeName) {
            RtlFreeHeap(RtlProcessHeap(), 0, fullPath);

            if (volumeName[0] == '\\' && volumeName[1] == '?' &&
                volumeName[2] == '?' && volumeName[3] == '\\') {

                if (volumeName[4] && volumeName[5] == ':') {
                    RtlInitUnicodeString(&name, volumeName);
                    MoveMemory(volumeName, volumeName + 4,
                               name.Length - 3*sizeof(WCHAR));
                } else {
                    volumeName[1] = '\\';
                }

                b = GetVolumePathNameW(volumeName, lpszVolumePathName,
                                       cchBufferLength);

            } else {

                RtlInitUnicodeString(&name, volumeName);
                RtlInitUnicodeString(&prefix, L"\\\\?\\GLOBALROOT");
                dosName.Length = name.Length + prefix.Length;
                dosName.MaximumLength = dosName.Length + sizeof(WCHAR);
                dosName.Buffer = RtlAllocateHeap(RtlProcessHeap(),
                                                 MAKE_TAG(TMP_TAG),
                                                 dosName.MaximumLength);
                if (!dosName.Buffer) {
                    RtlFreeHeap(RtlProcessHeap(), 0, volumeName);
                    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                    return FALSE;
                }

                CopyMemory(dosName.Buffer, prefix.Buffer, prefix.Length);
                CopyMemory((PCHAR) dosName.Buffer + prefix.Length,
                           name.Buffer, name.Length);
                dosName.Buffer[dosName.Length/sizeof(WCHAR)] = 0;

                b = GetVolumePathNameW(dosName.Buffer, lpszVolumePathName,
                                       cchBufferLength);

                RtlFreeHeap(RtlProcessHeap(), 0, dosName.Buffer);
            }

            RtlFreeHeap(RtlProcessHeap(), 0, volumeName);

            return b;
        }

        if (!resultOfOpen && p) {
            *p = c;
            RtlInitUnicodeString(&name, fullPath);
            break;
        }

        if (name.Length <= sizeof(WCHAR)) {
            break;
        }

        for (i = name.Length/sizeof(WCHAR) - 2; i > 0; i--) {
            if (name.Buffer[i] == '\\') {
                break;
            }
        }
        if (!i) {
            break;
        }

        if (resultOfOpen) {
            p = &name.Buffer[i + 1];
            c = *p;
            *p = 0;
        } else {
            name.Buffer[i + 1] = 0;
        }

        RtlInitUnicodeString(&name, fullPath);
    }

    RtlFreeHeap(RtlProcessHeap(), 0, volumeName);

    if (!resultOfOpen && !p) {
        RtlFreeHeap(RtlProcessHeap(), 0, fullPath);
        return FALSE;
    }

    if (cchBufferLength*sizeof(WCHAR) < name.Length + sizeof(WCHAR)) {
        RtlFreeHeap(RtlProcessHeap(), 0, fullPath);
        SetLastError(ERROR_FILENAME_EXCED_RANGE);
        return FALSE;
    }

    RtlCopyMemory(lpszVolumePathName, name.Buffer, name.Length);
    lpszVolumePathName[name.Length/sizeof(WCHAR)] = 0;
    RtlFreeHeap(RtlProcessHeap(), 0, fullPath);

    return TRUE;
}

BOOL
GetVolumePathNamesForVolumeNameA(
    LPCSTR lpszVolumeName,
    LPSTR lpszVolumePathNames,
    DWORD cchBufferLength,
    PDWORD lpcchReturnLength
    )

{
    PUNICODE_STRING unicodeVolumeName;
    ANSI_STRING     ansiVolumePathNames;
    UNICODE_STRING  unicodeVolumePathNames;
    BOOL            b;
    NTSTATUS        status;
    DWORD           len;

    unicodeVolumeName = Basep8BitStringToStaticUnicodeString(lpszVolumeName);
    if (!unicodeVolumeName) {
        return FALSE;
    }

    ansiVolumePathNames.Buffer = lpszVolumePathNames;
    ansiVolumePathNames.MaximumLength = (USHORT) cchBufferLength;
    unicodeVolumePathNames.Buffer = NULL;
    unicodeVolumePathNames.MaximumLength = 0;

    try {

        unicodeVolumePathNames.MaximumLength =
                ansiVolumePathNames.MaximumLength*sizeof(WCHAR);
        if (unicodeVolumePathNames.MaximumLength) {
            unicodeVolumePathNames.Buffer =
                    RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                    unicodeVolumePathNames.MaximumLength);
            if (!unicodeVolumePathNames.Buffer) {
                SetLastError(ERROR_NOT_ENOUGH_MEMORY);
                return FALSE;
            }
        } else {
            unicodeVolumePathNames.Buffer = NULL;
        }

        b = GetVolumePathNamesForVolumeNameW(unicodeVolumeName->Buffer,
                                             unicodeVolumePathNames.Buffer,
                                             cchBufferLength, &len);

        if (b || GetLastError() == ERROR_MORE_DATA) {

            if (b) {
                unicodeVolumePathNames.Length = (USHORT) len*sizeof(WCHAR);
            } else {
                unicodeVolumePathNames.Length = (USHORT)
                        cchBufferLength*sizeof(WCHAR);
            }

            status = BasepUnicodeStringTo8BitString(&ansiVolumePathNames,
                                                    &unicodeVolumePathNames,
                                                    FALSE);
            if (!NT_SUCCESS(status)) {
                BaseSetLastNTError(status);
                return FALSE;
            }

            if (lpcchReturnLength) {
                if (b) {
                    *lpcchReturnLength = ansiVolumePathNames.Length/sizeof(WCHAR);
                } else {
                    // Give an upper bound for the ANSI length since we
                    // don't actually know it.
                    *lpcchReturnLength = 2*len;
                }
            }
        }

    } finally {

        if (unicodeVolumePathNames.Buffer) {
            RtlFreeHeap(RtlProcessHeap(), 0, unicodeVolumePathNames.Buffer);
        }
    }

    return b;
}

BOOL
GetVolumePathNamesForVolumeNameW(
    LPCWSTR lpszVolumeName,
    LPWSTR lpszVolumePathNames,
    DWORD cchBufferLength,
    PDWORD lpcchReturnLength
    )

/*++

Routine Description:

    This routine returns a Multi-Sz list of volume path names for the
    given volume name.  The returned 'lpcchReturnLength' will include the
    extra tailing null characteristic of a Multi-Sz unless ERROR_MORE_DATA
    is returned in which case the list returned is as long as possible
    and may contain a part of a volume path.

Arguments:

    lpszVolumeName      - Supplies the volume name.

    lpszVolumePathNames - Returns the volume path names.

    cchBufferLength     - Supplies the size of the return buffer.

    lpcchReturnLength   - Returns the number of characters copied back to the
                            return buffer on success or the total number
                            of characters necessary for the buffer on
                            ERROR_MORE_DATA.

Return Value:

    FALSE   - Failure.

    TRUE    - Success.

--*/

{
    UNICODE_STRING          unicodeVolumeName;
    PMOUNTMGR_TARGET_NAME   targetName;
    HANDLE                  h;
    BOOL                    b;
    DWORD                   bytes, len, i, j, n;
    PMOUNTMGR_VOLUME_PATHS  volumePaths;

    RtlInitUnicodeString(&unicodeVolumeName, lpszVolumeName);
    if (unicodeVolumeName.Buffer[unicodeVolumeName.Length/sizeof(WCHAR) - 1] !=
        '\\') {

        BaseSetLastNTError(STATUS_OBJECT_NAME_INVALID);
        return FALSE;
    }

    if (!MOUNTMGR_IS_DOS_VOLUME_NAME_WB(&unicodeVolumeName)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

    targetName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                 MAX_PATH*sizeof(WCHAR));
    if (!targetName) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    ZeroMemory(targetName, MAX_PATH*sizeof(WCHAR));
    targetName->DeviceNameLength = unicodeVolumeName.Length - sizeof(WCHAR);
    RtlCopyMemory(targetName->DeviceName, lpszVolumeName,
                  targetName->DeviceNameLength);
    targetName->DeviceName[1] = '?';

    h = CreateFileW(MOUNTMGR_DOS_DEVICE_NAME, 0, FILE_SHARE_READ |
                    FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE);
    if (h == INVALID_HANDLE_VALUE) {
        RtlFreeHeap(RtlProcessHeap(), 0, targetName);
        return FALSE;
    }

    len = sizeof(MOUNTMGR_VOLUME_PATHS);
    volumePaths = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG), len);
    if (!volumePaths) {
        CloseHandle(h);
        RtlFreeHeap(RtlProcessHeap(), 0, targetName);
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return FALSE;
    }

    for (;;) {

        b = DeviceIoControl(h, IOCTL_MOUNTMGR_QUERY_DOS_VOLUME_PATHS,
                            targetName, MAX_PATH*sizeof(WCHAR), volumePaths,
                            len, &bytes, NULL);
        if (b) {
            break;
        }

        if (GetLastError() != ERROR_MORE_DATA) {
            RtlFreeHeap(RtlProcessHeap(), 0, volumePaths);
            RtlFreeHeap(RtlProcessHeap(), 0, targetName);
            return FALSE;
        }

        len = sizeof(MOUNTMGR_VOLUME_PATHS) + volumePaths->MultiSzLength;
        RtlFreeHeap(RtlProcessHeap(), 0, volumePaths);
        volumePaths = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TMP_TAG),
                                      len);
        if (!volumePaths) {
            CloseHandle(h);
            RtlFreeHeap(RtlProcessHeap(), 0, targetName);
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return FALSE;
        }
    }

    CloseHandle(h);
    RtlFreeHeap(RtlProcessHeap(), 0, targetName);

    n = 0;
    for (i = 0, j = 0; i < cchBufferLength &&
         j < volumePaths->MultiSzLength/sizeof(WCHAR) - 1; i++, j++) {

        if (!volumePaths->MultiSz[j]) {
            n++;
            lpszVolumePathNames[i++] = '\\';
            if (i == cchBufferLength) {
                break;
            }
        }

        lpszVolumePathNames[i] = volumePaths->MultiSz[j];
    }

    for (; j < volumePaths->MultiSzLength/sizeof(WCHAR) - 1; j++) {
        if (!volumePaths->MultiSz[j]) {
            n++;
        }
    }

    if (i < cchBufferLength) {
        b = TRUE;
        lpszVolumePathNames[i++] = 0;
        if (lpcchReturnLength) {
            *lpcchReturnLength = i;
        }
    } else {
        b = FALSE;
        SetLastError(ERROR_MORE_DATA);
        if (lpcchReturnLength) {
            *lpcchReturnLength = volumePaths->MultiSzLength/sizeof(WCHAR) + n;
        }
    }

    RtlFreeHeap(RtlProcessHeap(), 0, volumePaths);

    return b;
}