/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    dllfsd.c

Abstract:

    This module implements the OS/2 V2.0 fsd-related APIs: DosQueryFsInfo,
    DosSetFsInfo

Author:

    Therese Stowell (thereses) 17-Jan-1990

Revision History:

    Patrick Questembert (patrickq) 2-Feb-1992:
      Fixed & enhanced the DosQueryFSAttach call.

--*/

#define INCL_OS2V20_ERRORS
#define INCL_OS2V20_FSD
#include "os2dll.h"
#include "os2win.h"

// macro to probe string arguments passed to APIs
#define PROBE_STRING(s)     ((VOID) ((s) == NULL ? 0 : strlen(s)))


APIRET
ProcessLabel(
    IN PVOLUMELABEL Buffer,
    IN ULONG Length,
    OUT PFILE_FS_LABEL_INFORMATION LabelBuffer
    );

APIRET
OpenDrive(
    IN ULONG DiskNumber,
    IN ULONG RequestedAccess,
    OUT PHANDLE DriveHandle
    );

APIRET
DosSetCurrentDir(
    IN PSZ DirectoryName
    );

APIRET
DosQueryFSInfo(
    IN ULONG DiskNumber,
    IN ULONG FsInformationLevel,
    IN PBYTE Buffer,
    IN ULONG Length
    )

/*++

Routine Description:

    This routine returns allocation or label information for a volume.

Arguments:

    DiskNumber - which volume to return information for

    FsInformationLevel - what type of information to return

    Buffer - where to return information

    Length - length of buffer

Return Value:

    ERROR_INVALID_LEVEL - invalid FsInformationLevel

    ERROR_INVALID_DRIVE - specified volume does not exist

    ERROR_BUFFER_OVERFLOW - requested information won't fit in buffer

--*/

{
    APIRET RetCode;
    NTSTATUS Status;
    HANDLE DiskHandle;
    IO_STATUS_BLOCK IoStatus;
    struct LABEL_BUFFER {
        FILE_FS_VOLUME_INFORMATION VolumeInfo;
        WCHAR Label[MAX_LABEL_LENGTH];
    } VolInfoBuffer;
    FILE_FS_SIZE_INFORMATION FsSizeInfoBuffer;
    PFSINFO FsInfoBuf;
    PFSALLOCATE FsAllocBuf;
    STRING LabelString;
    UNICODE_STRING LabelString_U;
    ULONG i;

    if (FsInformationLevel > FSIL_VOLSER) {
        return ERROR_INVALID_LEVEL;
    }
    if (DiskNumber > MAX_DRIVES) {
        return ERROR_INVALID_DRIVE;
    }
    RetCode = OpenDrive(DiskNumber,
                        FILE_READ_DATA,
                        &DiskHandle
                       );
    if (RetCode != NO_ERROR) {
        return RetCode;
    }
    if (FsInformationLevel == FSIL_ALLOC) {
        if (Length < sizeof(FSALLOCATE)) {
            NtClose(DiskHandle);
            return ERROR_BUFFER_OVERFLOW;
        }
        do {
            Status = NtQueryVolumeInformationFile(DiskHandle,
                                                  &IoStatus,
                                                  &FsSizeInfoBuffer,
                                                  sizeof(FsSizeInfoBuffer),
                                                  FileFsSizeInformation
                                                 );
        } while (RetryIO(Status, DiskHandle));
        NtClose(DiskHandle);
        if (!NT_SUCCESS(Status)) {
            return ERROR_INVALID_DRIVE;
        }
        FsAllocBuf = (PFSALLOCATE) Buffer;
        try {
            FsAllocBuf->ulReserved = 0;
            FsAllocBuf->cSectorUnit = FsSizeInfoBuffer.SectorsPerAllocationUnit;
            // BUGBUG -  what do we do here if .HighPart is non-zero
            FsAllocBuf->cUnit = FsSizeInfoBuffer.TotalAllocationUnits.LowPart;
            FsAllocBuf->cUnitAvail = FsSizeInfoBuffer.AvailableAllocationUnits.LowPart;
            FsAllocBuf->cbSector = BYTES_PER_SECTOR;
        } except( EXCEPTION_EXECUTE_HANDLER ) {
           Od2ExitGP();
        }
    }
    else {
        if (Length < (FIELD_OFFSET(FSINFO, vol) + 1)) {
            NtClose(DiskHandle);
            return ERROR_BUFFER_OVERFLOW;
        }
        do {
            Status = NtQueryVolumeInformationFile(DiskHandle,
                                                  &IoStatus,
                                                  &VolInfoBuffer,
                                                  sizeof(VolInfoBuffer),
                                                  FileFsVolumeInformation
                                                 );
        } while (RetryIO(Status, DiskHandle));
        NtClose(DiskHandle);
        if (!NT_SUCCESS(Status)) {
            return ERROR_INVALID_DRIVE;
        }
        if (Length < (FIELD_OFFSET(FSINFO, vol)+(VolInfoBuffer.VolumeInfo.VolumeLabelLength / 2))) {
            return ERROR_BUFFER_OVERFLOW;
        }
        FsInfoBuf = (PFSINFO) Buffer;
        try {
            for (i = 0; i < 12; i++) {
                FsInfoBuf->vol.szVolLabel[i] = 0;
            }
            FsInfoBuf->ulVSN = VolInfoBuffer.VolumeInfo.VolumeSerialNumber;

            LabelString_U.Length = (USHORT)(VolInfoBuffer.VolumeInfo.VolumeLabelLength);
            LabelString_U.MaximumLength = (USHORT)(LabelString_U.Length +(USHORT)1);
            LabelString_U.Buffer = VolInfoBuffer.VolumeInfo.VolumeLabel;
            LabelString.Length = (USHORT)VolInfoBuffer.VolumeInfo.VolumeLabelLength;
            LabelString.MaximumLength = LabelString.Length + (USHORT)1;
            LabelString.Buffer = FsInfoBuf->vol.szVolLabel;
            RetCode = Od2UnicodeStringToMBString(&LabelString,
                        &LabelString_U,
                        FALSE);
            FsInfoBuf->vol.cch = (BYTE) LabelString.Length;
#if DBG
            IF_OD2_DEBUG( FSD ) {
                DbgPrint("volume label length is %d and label is %s\n",FsInfoBuf->vol.cch,FsInfoBuf->vol.szVolLabel);
            }
#endif
        } except( EXCEPTION_EXECUTE_HANDLER ) {
           Od2ExitGP();
        }
    }
    return RetCode;
}

APIRET
DosSetFSInfo(
    ULONG DiskNumber,
    ULONG FsInformationLevel,
    PBYTE Buffer,
    ULONG Length
    )

/*++

Routine Description:

    This routine sets label information for a volume.

Arguments:

    DiskNumber - which volume to set information for

    FsInformationLevel - what type of information to set

    Buffer - information to set

    Length - length of buffer

Return Value:

    ERROR_INVALID_LEVEL - invalid FsInformationLevel

    ERROR_INVALID_DRIVE - specified volume does not exist

    ERROR_INSUFFICIENT_BUFFER - buffer is too small to contain specified
    information

--*/

{
    APIRET RetCode;
    NTSTATUS Status;
    HANDLE DiskHandle;
    IO_STATUS_BLOCK IoStatus;
    struct LABEL_BUFFER {
        FILE_FS_LABEL_INFORMATION LabelInfo;
        WCHAR Label[MAX_LABEL_LENGTH];
    } LabelBuffer;

    if (DiskNumber > MAX_DRIVES) {
        return ERROR_INVALID_DRIVE;
    }
    if (FsInformationLevel != FSIL_VOLSER) {
        return ERROR_INVALID_LEVEL;
    }
    RetCode = OpenDrive(DiskNumber,
                        FILE_WRITE_DATA,
                        &DiskHandle
                       );
    if (RetCode != NO_ERROR) {
        return RetCode;
    }
    RetCode = ProcessLabel((PVOLUMELABEL) Buffer,
                           Length,
                           (PFILE_FS_LABEL_INFORMATION) &LabelBuffer
                          );
    if (RetCode != NO_ERROR) {
        NtClose(DiskHandle);
        return RetCode;
    }
    do {
        Status = NtSetVolumeInformationFile(DiskHandle,
                                            &IoStatus,
                                            &LabelBuffer,
                                            sizeof(LabelBuffer),
                                            FileFsLabelInformation
                                           );
    } while (RetryIO(Status, DiskHandle));
    NtClose(DiskHandle);
    if (!NT_SUCCESS(Status)) {
        if (Status == STATUS_INVALID_VOLUME_LABEL)
            return ERROR_INVALID_NAME;
        else
            return ERROR_INVALID_DRIVE;
    }
    return NO_ERROR;
}


APIRET
OpenDrive(
    IN ULONG DiskNumber,
    IN ULONG RequestedAccess,
    OUT PHANDLE DriveHandle
    )

/*++

Routine Description:

    This routine opens a drive and returns a handle to it.

Arguments:

    DiskNumber - which volume to open

    RequestedAccess - requested open access to drive

    DriveHandle - where to return open handle to drive

Return Value:

    ERROR_INVALID_DRIVE - specified volume does not exist

--*/

{
    CHAR DiskName[] = "\\OS2SS\\DRIVES\\D:\\";
    STRING DiskNameString;
    UNICODE_STRING DiskNameString_U;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES Obja;
    IO_STATUS_BLOCK IoStatus;
    APIRET   RetCode;


    if (DiskNumber == 0)    // default drive
        DiskNumber = Od2CurrentDisk;
    else
        DiskNumber -= 1;    // make drive 0-based
    // BUGBUG when we add remote drives, need to fix this
    DiskName[DRIVE_LETTER+FILE_PREFIX_LENGTH] = (CHAR) (DiskNumber + 'A');
    Od2InitMBString(&DiskNameString,DiskName);

        //
        // UNICODE conversion -
        //

    RetCode = Od2MBStringToUnicodeString(
                    &DiskNameString_U,
                    &DiskNameString,
                    TRUE);

    if (RetCode)
    {
#if DBG
        IF_OD2_DEBUG( FILESYS )
        {
            DbgPrint("OpenDrive: no memory for Unicode Conversion\n");
        }
#endif
        return RetCode;
    }

    InitializeObjectAttributes(&Obja,
                &DiskNameString_U,
                OBJ_CASE_INSENSITIVE,
                NULL,
                NULL);
    do {
        Status = NtOpenFile(DriveHandle,
                            RequestedAccess | SYNCHRONIZE,
                            &Obja,
                            &IoStatus,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            FILE_SYNCHRONOUS_IO_NONALERT
                            );
    } while (RetryCreateOpen(Status, &Obja));
    RtlFreeUnicodeString (&DiskNameString_U);
    if (!NT_SUCCESS(Status)) {
        return ERROR_INVALID_DRIVE;
    }
    return NO_ERROR;
}

APIRET
ProcessLabel(
    IN PVOLUMELABEL Buffer,
    IN ULONG Length,
    OUT PFILE_FS_LABEL_INFORMATION LabelBuffer
    )

/*++

Routine Description:

    This routine checks a volume label for validity and copies it to an
    NT buffer.

Arguments:

    Buffer - buffer containing os/2 format volume label (unprobed)

    Length - length of buffer

    LabelBuffer - where to store processed label

Return Value:

    ERROR_INSUFFICIENT_BUFFER - buffer is too small to contain the label

    ERROR_LABEL_TOO_LONG - the volume label is too long

--*/

{
    STRING   LabelString;
    APIRET   RetCode;
    UNICODE_STRING LabelString_U;

    if (!Length)
        return ERROR_INSUFFICIENT_BUFFER;
    try {
        LabelBuffer->VolumeLabelLength = Buffer->cch;
        if ((LabelBuffer->VolumeLabelLength+1) > Length)
            return ERROR_INSUFFICIENT_BUFFER;
        if (LabelBuffer->VolumeLabelLength > MAX_LABEL_LENGTH)
            return ERROR_LABEL_TOO_LONG;

        Od2InitMBString(&LabelString,(PSZ)(Buffer->szVolLabel));
#ifdef DBCS
// MSKK Nov.04.1993 V-AkihiS
        LabelString.Length = Buffer->cch;
#endif


        //
        // UNICODE conversion -
        //

        RetCode = Od2MBStringToUnicodeString(
                    &LabelString_U,
                    &LabelString,
                    TRUE);

        if (RetCode)
        {
#if DBG
            IF_OD2_DEBUG( FILESYS )
            {
                DbgPrint("ProcessLabel: no memory for Unicode Conversion\n");
            }
#endif
            return RetCode;
        }

#ifdef DBCS
// MSKK Apr.18.1993 V-AkihiS
        //
        // Use UNICODE string length as volume label Length for DBCS support.
        //
        LabelBuffer->VolumeLabelLength = LabelString_U.Length;
#else
                //
                // Since we are talking WCHAR, double length
                //
        LabelBuffer->VolumeLabelLength = LabelBuffer->VolumeLabelLength * 2;
#endif

        RtlMoveMemory(&(LabelBuffer->VolumeLabel),
                LabelString_U.Buffer,
                LabelBuffer->VolumeLabelLength);

        RtlFreeUnicodeString (&LabelString_U);

    } except( EXCEPTION_EXECUTE_HANDLER ) {
       Od2ExitGP();
    }
    return NO_ERROR;
}

APIRET
DosFSCtl(
    IN PBYTE Data,
    IN ULONG DataLength,
    OUT PULONG ActualDataLength,
    IN PBYTE Parameters,
    IN ULONG ParametersLength,
    IN OUT PULONG ActualParametersLength,
    IN ULONG Function,
    IN PSZ RouteName,
    IN HFILE FileHandle,
    IN ULONG RoutingMethod
    )

/*++

Routine Description:


Arguments:


Return Value:

BUGBUG whenever this is sorted out, finish probing parms

--*/

{
#if 0
    HANDLE NtHandle;
    NTSTATUS Status;
    APIRET RetCode;
    STRING CanonicalNameString;
    UNICODE_STRING CanonicalNameString_U;
    ULONG FileType;
    ULONG FileFlags;
    OBJECT_ATTRIBUTES Obja;
    IO_STATUS_BLOCK IoStatus;
    PFILE_HANDLE hFileRecord;
#endif
    #if DBG
    PSZ RoutineName;
    RoutineName = "DosFSCtl";
    #endif

    UNREFERENCED_PARAMETER(ParametersLength);

    UNREFERENCED_PARAMETER(Data);
    UNREFERENCED_PARAMETER(DataLength);
    UNREFERENCED_PARAMETER(ActualDataLength);
    UNREFERENCED_PARAMETER(Parameters);
    UNREFERENCED_PARAMETER(ActualParametersLength);
    UNREFERENCED_PARAMETER(Function);
    UNREFERENCED_PARAMETER(RouteName);
    UNREFERENCED_PARAMETER(FileHandle);
    UNREFERENCED_PARAMETER(RoutingMethod);

    return ERROR_INVALID_FUNCTION;

#if 0
    if ((RoutingMethod < FSCTL_HANDLE) || (RoutingMethod > FSCTL_FSDNAME))
        return ERROR_INVALID_LEVEL;

    if (RoutingMethod == FSCTL_HANDLE) {
        if (RouteName != NULL)
            return ERROR_INVALID_PARAMETER;
        AcquireFileLockShared(
                          #if DBG
                          RoutineName
                          #endif
                         );
        RetCode = DereferenceFileHandle(FileHandle,&hFileRecord);
        if (RetCode != NO_ERROR) {
            ReleaseFileLockShared(
                          #if DBG
                          RoutineName
                          #endif
                         );
            return RetCode;
        }
        if (hFileRecord->FileType &
              (FILE_TYPE_DEV | FILE_TYPE_PIPE | FILE_TYPE_NMPIPE)) {
              // BUGBUG at some point, NMPIPE and DEV will probably be ok
            ReleaseFileLockShared(
                          #if DBG
                          RoutineName
                          #endif
                         );
            return ERROR_INVALID_HANDLE;
        }
        else {
            NtHandle = hFileRecord->NtHandle;
        }
    }
    else if (RoutingMethod == FSCTL_PATHNAME) {
        if (FileHandle != (HFILE) -1)
            return ERROR_INVALID_PARAMETER;
        // BUGBUG need to append \ if d:
        RetCode = Od2Canonicalize(RouteName,
                                  CANONICALIZE_FILE_DEV_OR_PIPE,
                                  &CanonicalNameString,
                                  &NtHandle,
                                  &FileFlags, // BUGBUG should we fail if root?
                                  &FileType
                                 );
        if (RetCode != NO_ERROR) {
            return ERROR_INVALID_PATH;
        }
        if (FileType & (FILE_TYPE_DEV | FILE_TYPE_PSDEV | FILE_TYPE_NMPIPE)) {
            RtlFreeHeap(Od2Heap, 0,CanonicalNameString.Buffer);
            return ERROR_INVALID_PATH;
        }
#if DBG
        IF_OD2_DEBUG( FSD ) {
            DbgPrint("canonicalize returned %s\n",CanonicalNameString.Buffer);
        }
#endif
        //
        // UNICODE conversion -
        //

        RetCode = Od2MBStringToUnicodeString(
                    &CanonicalNameString_U,
                    &CanonicalNameString,
                    TRUE);

        if (RetCode)
        {
#if DBG
            IF_OD2_DEBUG( FILESYS )
            {
                DbgPrint("DosFSCtl: no memory for Unicode Conversion\n");
            }
#endif
            RtlFreeHeap(Od2Heap, 0,CanonicalNameString.Buffer);
            return RetCode;
        }

        InitializeObjectAttributes(&Obja,
                                   &CanonicalNameString_U,
                                   OBJ_CASE_INSENSITIVE,
                                   NtHandle,
                                   NULL);

        do {
            Status = NtOpenFile(&NtHandle,
                                SYNCHRONIZE,
                                &Obja,
                                &IoStatus,
                                FILE_SHARE_WRITE | FILE_SHARE_READ,
                                FILE_SYNCHRONOUS_IO_NONALERT
                                );
        } while (RetryCreateOpen(Status, &Obja));
        RtlFreeUnicodeString (&CanonicalNameString_U);


        if (!(NT_SUCCESS(Status))) {
            RtlFreeHeap(Od2Heap, 0,CanonicalNameString.Buffer);
#if DBG
            IF_OD2_DEBUG( FSD ) {
                DbgPrint("NtOpenFile returned %X\n",Status);
            }
#endif
            return ERROR_PATH_NOT_FOUND;    // BUGBUG bogus error
        }
    }
    else {  // FSCTL_FSDNAME
        if (FileHandle != (HFILE) -1)
            return ERROR_INVALID_PARAMETER;
        try {
            Od2InitMBString(&CanonicalNameString,RouteName);
        } except( EXCEPTION_EXECUTE_HANDLER ) {
           Od2ExitGP();
        }
        //
        // UNICODE conversion -
        //

        RetCode = Od2MBStringToUnicodeString(
                    &CanonicalNameString_U,
                    &CanonicalNameString,
                    TRUE);

        if (RetCode)
        {
#if DBG
            IF_OD2_DEBUG( FILESYS )
            {
                DbgPrint("DosFSCtl: no memory for Unicode Conversion-2\n");
            }
#endif
            return RetCode;
        }

        InitializeObjectAttributes(&Obja,
                                   &CanonicalNameString_U,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);

        do {
            Status = NtOpenFile(&NtHandle,
                                SYNCHRONIZE,
                                &Obja,
                                &IoStatus,
                                FILE_SHARE_WRITE | FILE_SHARE_READ,
                                FILE_SYNCHRONOUS_IO_NONALERT
                                );
        } while (RetryCreateOpen(Status, &Obja));
        RtlFreeUnicodeString (&CanonicalNameString_U);

        if (!(NT_SUCCESS(Status))) {
#if DBG
            IF_OD2_DEBUG( FSD ) {
                DbgPrint("NtOpenFile returned %X\n",Status);
            }
#endif
            return ERROR_INVALID_FSD_NAME;
        }
    }

    Status = NtFsControlFile(NtHandle,
                             (HANDLE) NULL,
                             (PIO_APC_ROUTINE) NULL,
                             (PVOID) NULL,
                             &IoStatus,
                             Function,
                             (PVOID) Parameters,
                             *ActualParametersLength,
                             (PVOID) Data,
                             DataLength
                            );

    if (RoutingMethod == FSCTL_HANDLE) {
        ReleaseFileLockShared(
                          #if DBG
                          RoutineName
                          #endif
                         );
    }
    else if (RoutingMethod == FSCTL_PATHNAME) {
        NtClose(NtHandle);
        RtlFreeHeap(Od2Heap, 0,CanonicalNameString.Buffer);
    }
    else {  // FSCTL_FSDNAME
        NtClose(NtHandle);
    }
    if (!NT_SUCCESS(Status)) {
        if (Status == STATUS_BUFFER_OVERFLOW)
            return ERROR_BUFFER_OVERFLOW;
        else
            return ERROR_INVALID_FUNCTION;  // BUGBUG errors should be mapped
    }
    else {
        try {
            *ActualDataLength = IoStatus.Information;
        } except( EXCEPTION_EXECUTE_HANDLER ) {
           Od2ExitGP();
        }
        return NO_ERROR;
    }
#endif
}


APIRET
DosFSAttach(
    IN PSZ DeviceName,
    IN PSZ FsName,
    IN PBYTE FsData,
    IN ULONG FsDataLength,
    IN ULONG AttachFlags
    )

/*++

Routine Description:

    This function partially implements the DosFSAttach() API.  The only valid FsName is "LAN".
    The format expected for the arguments is as follows:

        DeviceName -- device name, e.g. "J:", "LPT1:"
        FsName -- "LAN"
        FsData -- either "\01\0SHARENAME" for a regular connection or
                         "\02\0SHARENAME\0PASSWORD" for a password connection or
                         "\03\0SHARENAME\0PASSWORD\0USERNAME" for a username/password connection.
        FsDataLength -- length of FsData
        AttachFlags -- FS_ATTACH or FS_DETACH

    The function connects to the network using WNetAdd/DelConnection().  Therefore it'll use the
    multiple provider router to connect to any type of network for which NT has a redirector.
    The SHARENAME format depends on the network you're trying to reach.  for LanMan/MsNet networks
    it's "\\\\sharename\\servername".

    If DeviceName is a drive letter, the drive is automatically reset to the root directory after
    a connection, and before a disconnection.  This is for compatibility with OS/2.

Arguments:

    see above + documentation of DosFSAttach().

Return Value:

    error status.

--*/

{
    NETRESOURCEA netr;
    PSZ passwd = NULL;
    PSZ username = NULL;
    ULONG Drive = (ULONG)-1;
    ULONG Length;
    ULONG  rc;
    APIRET nrc;
    CHAR DriveBuf[4];

    try {
        PROBE_STRING(DeviceName);
        PROBE_STRING(FsName);
        Od2ProbeForRead(FsData, FsDataLength, 1);
    } except( EXCEPTION_EXECUTE_HANDLER ) {
       Od2ExitGP();
    }

    if (FsName == NULL ||
        _stricmp(FsName, "LAN") != 0) {
#if DBG
        IF_OD2_DEBUG( FSD ) {
            KdPrint(("DosFSAttach: Invalid FSD name\n"));
        }
#endif
        return(ERROR_INVALID_FSD_NAME);
    }

    if (DeviceName != NULL &&
        DeviceName[0] != '\0' &&
        DeviceName[1] == ':' &&
        DeviceName[2] == '\0') {        // is the device a disk drive?

        int ch;

        ch = toupper((UCHAR) DeviceName[0]);

        if (isalpha(ch)) {

            Drive = (ULONG) (ch - 'A');

            if (Od2CurrentDisk == Drive) {
#if DBG
                IF_OD2_DEBUG( FSD ) {
                    KdPrint(("DosFSAttach: attempt to attach/detach current drive = %ld\n", Drive));
                }
#endif
                return(ERROR_DEVICE_IN_USE);
            }

            DriveBuf[0] = (UCHAR) ch;
            DriveBuf[1] = ':';
            DriveBuf[2] = '\\';
            DriveBuf[3] = '\0';

        } else {
#if DBG
            IF_OD2_DEBUG( FSD ) {
                KdPrint(("DosFSAttach: Invalid drive letter = %c\n", ch));
            }
#endif
            return(ERROR_INVALID_DRIVE);
        }
    }

    if (AttachFlags == FS_ATTACH) {

        if (FsDataLength < 3 ||
            FsData[0] == 0 ||
            FsData[0] > 3 ||
            FsData[1] != 0) {
#if DBG
            IF_OD2_DEBUG( FSD ) {
                KdPrint(("DosFSAttach: Unsupported FsData structure\n"));
            }
#endif
            return(ERROR_NOT_SUPPORTED);
        }

        netr.lpLocalName = DeviceName;
        netr.lpRemoteName = (PSZ) FsData + 2;

        try {

            Length = strlen(netr.lpRemoteName);

        } except( EXCEPTION_EXECUTE_HANDLER ) {
           Od2ExitGP();
        }

        netr.lpProvider = NULL;
        netr.dwType = RESOURCETYPE_ANY;

        if (FsData[0] >= 2) {

            passwd = netr.lpRemoteName + Length + 1;
            if (FsData + FsDataLength <= passwd) {
#if DBG
                IF_OD2_DEBUG( FSD ) {
                    KdPrint(("DosFSAttach: FsData overflow\n"));
                }
#endif
                return(ERROR_INVALID_PATH);
            }

            try {

                Length = strlen(passwd);

            } except( EXCEPTION_EXECUTE_HANDLER ) {
               Od2ExitGP();
            }

            if (FsData[0] == 3) {

                username = passwd + Length + 1;
                if (FsData + FsDataLength <= username) {
#if DBG
                    IF_OD2_DEBUG( FSD ) {
                        KdPrint(("DosFSAttach: FsData overflow\n"));
                    }
#endif
                    return(ERROR_INVALID_PATH);
                }

                try {

                    (VOID) strlen(username);

                } except( EXCEPTION_EXECUTE_HANDLER ) {
                   Od2ExitGP();
                }
            }
        }

#if DBG
        IF_OD2_DEBUG( FSD ) {
            KdPrint(("DosFSAttach: calling WNetAddConnection2()\n"));
        }
#endif

        rc = WNetAddConnection2A(&netr,
                                 passwd,
                                 username,
                                 0);

        if (rc == NO_ERROR && Drive != (ULONG)-1) {

            //
            // initialize the connection to the root directory on target drive
            //

            (VOID) DosSetCurrentDir(DriveBuf);
        }

    } else if (AttachFlags == FS_DETACH) {

        if (Drive != (ULONG)-1) {

            //
            // go back to root directory on target drive
            //

            (VOID) DosSetCurrentDir(DriveBuf);
        }

#if DBG
        IF_OD2_DEBUG( FSD ) {
            KdPrint(("DosFSAttach: calling WNetCancelConnection2()\n"));
        }
#endif

        //
        // In the case that the disk that will be deattached is the current disk
        // (from Win32 point of view), change the current disk according to
        // OS2 ss (Od2CurrentDisk).
        //
		{
			PSZ pBuffer;
            DWORD length;

			length = GetCurrentDirectoryA(0, NULL);
			pBuffer = RtlAllocateHeap(Od2Heap, 0, length);
			GetCurrentDirectoryA(length, pBuffer);
			if (toupper(pBuffer[0]) == toupper(DeviceName[0])) {
				DriveBuf[0] = (UCHAR) Od2CurrentDisk + 'A';
				DriveBuf[1] = ':';
				DriveBuf[2] = '\0';
				SetCurrentDirectoryA(DriveBuf);
			}
			RtlFreeHeap(Od2Heap, 0, pBuffer);
		}

        rc = WNetCancelConnection2A(DeviceName,
                                    0,
                                    TRUE);
    } else {
#if DBG
        IF_OD2_DEBUG( FSD ) {
            KdPrint(("DosFSAttach: Invalid level\n"));
        }
#endif
        return(ERROR_INVALID_LEVEL);
    }

    switch (rc) {

        case NO_ERROR:
            nrc = NO_ERROR;
            break;

        case ERROR_ACCESS_DENIED:
        case ERROR_ALREADY_ASSIGNED:
        case ERROR_BAD_DEV_TYPE:
        case ERROR_INVALID_PASSWORD:
            nrc = (APIRET) rc;
            break;

        case 1200L:     /* ERROR_BAD_DEVICE */
        case 2250L:     /* ERROR_NOT_CONNECTED */
            nrc = ERROR_INVALID_DRIVE;
            break;

        case ERROR_BAD_NET_NAME:
        case 1203L:     /* ERROR_NO_NET_OR_BAD_PATH */
        case 2138L:     /* ERROR_NO_NETWORK */
            nrc = ERROR_INVALID_PATH;
            break;

        case 2404L:     /* ERROR_DEVICE_IN_USE */
        case 2401L:     /* ERROR_OPEN_FILES */
            nrc = ERROR_DEVICE_IN_USE;
            break;

        default:
            nrc = ERROR_INVALID_PATH;
            break;
    }

#if DBG
    IF_OD2_DEBUG( FSD ) {
        KdPrint(("DosFSAttach: rc = %lx, nrc = %x\n", rc, nrc));
    }
#endif

    return(nrc);
}

/* Dos32FsAttach() */
/* Attact or detach */
//#define FS_ATTACH       0       /* Attach file server */
//#define FS_DETACH       1       /* Detach file server */

APIRET
DosQueryFSAttach(
    IN PSZ DeviceName,
    IN ULONG Ordinal,
    IN ULONG FsInformationLevel,
    OUT PBYTE FsAttributes,
    IN OUT PULONG FsAttributesLength
    )

/*++

Routine Description:

  BUGBUG - Note that the FSAData[] field is left empty for local drives.
           Query by ordinal is not supported.

Arguments:


Return Value:


--*/

{
    ULONG DriveNumber;
    USHORT NameLength;
    PFSQBUFFER2 BufPtr;
    char *StrPtr;
    APIRET rc;

    if ((FsInformationLevel < FSAIL_QUERYNAME) ||
        (FsInformationLevel > FSAIL_DRVNUMBER)) {
        return ERROR_INVALID_LEVEL;
    }
    BufPtr = (PFSQBUFFER2) FsAttributes;
    try {
        if (*FsAttributesLength < (sizeof(FSQBUFFER)-1))
            return ERROR_BUFFER_OVERFLOW;
        if (FsInformationLevel == FSAIL_QUERYNAME)
        {
            NTSTATUS Status;
            STRING TargetCanonicalName;
            UNICODE_STRING TargetUnicodeName;
            ULONG TargetType;
            ULONG TargetFlags;
            OBJECT_ATTRIBUTES object_attributes;
            ULONG attr_len;
            char actual_file_name[256];
            /* Local parameters for the NtOpenFile() call */
            IO_STATUS_BLOCK io_status_block;
            HANDLE hand;
            /* For NtQueryInformationFile */
            char tmp_buf[1024];
            PFILE_FS_ATTRIBUTE_INFORMATION pFileFsInfo;
            FILE_FS_DEVICE_INFORMATION DeviceInfo;

            /* Handle the non X: case */
            if (DeviceName[COLON] != ':')
            {
                STRING TargetCanonicalName;
                ULONG TargetType;
                ULONG TargetFlags;

                /* If not a drive letter, then must be \DEV\xxx or
                   /DEV/xxx */
                if ((_strnicmp(DeviceName,"\\DEV\\",5)) &&
                    (_strnicmp(DeviceName,"/DEV/",5)))
                {
                    return ERROR_INVALID_PATH;
                }

                NameLength = (USHORT)strlen(DeviceName);

                rc = Od2Canonicalize(DeviceName,
                                  CANONICALIZE_FILE_OR_DEV,
                                  &TargetCanonicalName,
                                  NULL,
                                  &TargetFlags,
                                  &TargetType);

                if (rc != NO_ERROR)
                {
#if DBG
                    IF_OD2_DEBUG( FILESYS )
                    {
                        DbgPrint(
          "DosQueryFSAttach: Failed to Od2Canonicalize(\"%s\") - rc = %x\n",
                        DeviceName, rc);
                    }
#endif
                    return ERROR_INVALID_PATH;
                }

#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint("Canonical name is <%s>\n",
                              TargetCanonicalName.Buffer);
                }
#endif
                /* BUGBUG - Check meaning of code below (from 'dllcopy.c, DosCopy()) */
                if (TargetFlags & CANONICALIZE_META_CHARS_FOUND)
                {
#if DBG
                    IF_OD2_DEBUG( FILESYS )
                    {
                        DbgPrint("DosQueryFSAttach: <%s> has META_CHARS\n",
                            DeviceName);
                    }
#endif
                    RtlFreeHeap(Od2Heap, 0,TargetCanonicalName.Buffer);
                    return ERROR_INVALID_PATH;
                }

#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint("Type = <%x>, Flags = <%x>\n",
                        TargetType, TargetFlags);
                }
#endif

                if (!(TargetType & FILE_TYPE_DEV) &&
                    !(TargetType & FILE_TYPE_PSDEV) &&
                    !(TargetType & FILE_TYPE_COM))
                {
#if DBG
                    IF_OD2_DEBUG( FILESYS )
                    {
                        DbgPrint("DosQueryFSAttach: <%s> is not a valid device\n",
                            DeviceName);
                    }
#endif
                    RtlFreeHeap(Od2Heap, 0,TargetCanonicalName.Buffer);
                    return ERROR_INVALID_PATH;
                }

                /* Note that although some devices are marked as pseudo, under
                   OS/2 1.21, I found none such devices, hence the line below */
                BufPtr->iType = FSAT_CHARDEV;
                BufPtr->cbName = NameLength;
                BufPtr->cbFSDName = 0;
                BufPtr->cbFSAData = 0;
                StrPtr = BufPtr->szName;
                strncpy(StrPtr,DeviceName,NameLength+1);
                _strupr(StrPtr); /* Convert string to uppercase, just like under OS/2 1.x */
                StrPtr += NameLength+1;
                StrPtr[0] = '\0'; /* "" - no file-system name for \\dev\xxx */
                *FsAttributesLength = (ULONG)(StrPtr
                                       + BufPtr->cbFSDName + 1
                                       + BufPtr->cbFSAData
                                       - (PBYTE)BufPtr);
                RtlFreeHeap(Od2Heap, 0,TargetCanonicalName.Buffer);

                return NO_ERROR;
            }

            DriveNumber = (ULONG) (UCase(DeviceName[DRIVE_LETTER]) - 'A');
            if ((DeviceName[FIRST_SLASH] != '\0') ||
                (DriveNumber >= MAX_DRIVES))
            {
                return ERROR_INVALID_DRIVE;
            }

            strcpy(actual_file_name, DeviceName);
            strcat(actual_file_name,"\\");

            rc = Od2Canonicalize(actual_file_name,
                                  CANONICALIZE_FILE_OR_DEV,
                                  &TargetCanonicalName,
                                  NULL,
                                  &TargetFlags,
                                  &TargetType);

            if (rc != NO_ERROR)
            {
#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint(
          "DosQueryFSAttach: Failed to Od2Canonicalize(\"%s\") - rc = %x\n",
                        actual_file_name, rc);
                }
#endif
                return ERROR_INVALID_DRIVE;
            }

#if DBG
            IF_OD2_DEBUG( FILESYS )
            {
                DbgPrint("Canonical name is <%s>\n",
                          TargetCanonicalName.Buffer);
            }
#endif

            rc = Od2MBStringToUnicodeString(
                &TargetUnicodeName,
                &TargetCanonicalName,
                TRUE);

            if (rc)
            {
#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint("DosQueryFSAttach: no memory for Unicode Conversion\n");
                }
#endif
                RtlFreeHeap(Od2Heap, 0,TargetCanonicalName.Buffer);
                return rc;
            }

            RtlFreeHeap(Od2Heap, 0, TargetCanonicalName.Buffer);

            InitializeObjectAttributes(
                &object_attributes,
                &TargetUnicodeName,
                OBJ_CASE_INSENSITIVE,
                (HANDLE) NULL,
                NULL);

            do {
                Status = NtOpenFile(
                    &hand,
                    SYNCHRONIZE | FILE_READ_ATTRIBUTES,
                    &object_attributes,
                    &io_status_block,
                    FILE_SHARE_READ,
                    FILE_DIRECTORY_FILE
                    );
            } while (RetryCreateOpen(Status, &object_attributes));

            RtlFreeUnicodeString(&TargetUnicodeName);

            if (!NT_SUCCESS(Status))
            {
#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint(
          "DosQueryFSAttach: Failed to NtOpenFile (%s) - rc = %x\n",
                      DeviceName, Status);
                }
#endif
                return ERROR_INVALID_DRIVE;
            }

            do {
                Status = NtQueryVolumeInformationFile(
                    hand,
                    &io_status_block,
                    tmp_buf,
                    1024,
                    FileFsAttributeInformation
                    );
            } while (RetryIO(Status, hand));

            if (!NT_SUCCESS(Status))
            {
#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint(
          "DosQueryFSAttach: Failed to NtQueryVolumeInformation (%s) - rc = %x\n",
                        DeviceName, Status);
                }
#endif
                NtClose(hand);
                return ERROR_INVALID_DRIVE;
            }

            do {
                Status = NtQueryVolumeInformationFile(
                                hand,
                                &io_status_block,
                                &DeviceInfo,
                                sizeof (DeviceInfo),
                                FileFsDeviceInformation);
            } while (RetryIO(Status, hand));

            if (!NT_SUCCESS(Status))
            {
#if DBG
                IF_OD2_DEBUG( FILESYS )
                {
                    DbgPrint(
          "DosQueryFSAttach: Failed to NtQueryVolumeInformation (%s) - rc = %x\n",
                        DeviceName, Status);
                }
#endif
                NtClose(hand);
                return ERROR_INVALID_DRIVE;
            }

            pFileFsInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)tmp_buf;
            BufPtr->cbFSAData = 0;
            StrPtr = BufPtr->szName;
            strcpy(StrPtr,DeviceName);
            BufPtr->cbName = (USHORT)strlen(StrPtr);
            _strupr(StrPtr); /* Convert string to uppercase, just like under OS/2 1.x */
            StrPtr += BufPtr->cbName + 1;

            Od2nCopyWstrToStr(StrPtr, pFileFsInfo->FileSystemName,
                              pFileFsInfo->FileSystemNameLength/2);

            if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE)
            {
                // NT returns FAT, HPFS & NTFS (xlated above to HPFS) even for
                // LAN drives, so fix it to be 'LAN' as under OS/2
                strcpy(StrPtr, "LAN");
            }
            //Translate NTFS to HPFS (the closest OS/2 equivalent)
            else if (!strcmp(StrPtr, "NTFS"))
                strcpy(StrPtr, "HPFS");

            BufPtr->cbFSDName = (USHORT)strlen(StrPtr); /* File-system name length,
                                                   not including the \0 */

            if (DeviceInfo.Characteristics & FILE_REMOTE_DEVICE)
            {
                char tmp_buf[256];
                ULONG count = 256;

                BufPtr->iType = FSAT_REMOTEDRV;
                // Get the share name for the drive, if possible
                if (!WNetGetConnectionA(
                    BufPtr->szName,     // drive letter
                    tmp_buf,            // result buffer
                    &count))
                {
                    if ((ULONG)(StrPtr
                               + BufPtr->cbFSDName + 1
                               + BufPtr->cbFSAData
                               + strlen(tmp_buf) + 1
                               - (PBYTE)BufPtr) <=
                         *FsAttributesLength)
                    {
                        try
                        {
                            strcpy(StrPtr + BufPtr->cbFSDName + 1,
                                    tmp_buf);
#ifdef DBCS
// MSKK Jul.22.1993 V-AkihiS
// cbFSAData counts null.
                            BufPtr->cbFSAData = strlen(StrPtr + BufPtr->cbFSDName + 1) + 1;
#else
                            BufPtr->cbFSAData = strlen(StrPtr + BufPtr->cbFSDName + 1);
#endif
                        } except( EXCEPTION_EXECUTE_HANDLER )
                        {
                            // Just ignore error
                            BufPtr->cbFSAData = 0;  // Just in case ...
                        }
                    }
                }
            }
            else
                BufPtr->iType = FSAT_LOCALDRV;

            attr_len = (ULONG)(StrPtr
                               + BufPtr->cbFSDName + 1
                               + BufPtr->cbFSAData
                               - (PBYTE)BufPtr);

#if NEVER
            /* If one is interested, here's the full list: */
            switch (DeviceInfo.DeviceType)
            {
                 case FILE_DEVICE_CD_ROM:
                 case FILE_DEVICE_CD_ROM_FILE_SYSTEM:
                 case FILE_DEVICE_DISK:
                 case FILE_DEVICE_DISK_FILE_SYSTEM:
                 case FILE_DEVICE_FILE_SYSTEM:
                 case FILE_DEVICE_VIRTUAL_DISK:
                 case FILE_DEVICE_NETWORK:
                 case FILE_DEVICE_NETWORK_FILE_SYSTEM:
                 case FILE_DEVICE_TAPE:
                 case FILE_DEVICE_TAPE_FILE_SYSTEM:
                 case FILE_DEVICE_CONTROLLER:
                 case FILE_DEVICE_DATALINK:
                 case FILE_DEVICE_DFS:
                 case FILE_DEVICE_KEYBOARD:
                 case FILE_DEVICE_MAILSLOT:
                 case FILE_DEVICE_MOUSE:
                 case FILE_DEVICE_NAMED_PIPE:
                 case FILE_DEVICE_NULL:
                 case FILE_DEVICE_PARALLEL_PORT:
                 case FILE_DEVICE_PHYSICAL_NETCARD:
                 case FILE_DEVICE_PRINTER:
                 case FILE_DEVICE_SERIAL_PORT:
                 case FILE_DEVICE_SCREEN:
                 case FILE_DEVICE_SOUND:
                 case FILE_DEVICE_STREAMS:
                 case FILE_DEVICE_TRANSPORT:
                 case FILE_DEVICE_UNKNOWN:
                 case FILE_DEVICE_NETWORK_BROWSER:
                 case FILE_DEVICE_WAVE_IN:
                 case FILE_DEVICE_WAVE_OUT:
                 case FILE_DEVICE_8042_PORT:
                 case FILE_DEVICE_INPORT_PORT:
                 case FILE_DEVICE_SERIAL_MOUSE_PORT:
                 case FILE_DEVICE_BEEP:
                 case FILE_DEVICE_SCANNER:
                 case FILE_DEVICE_VIDEO:
                 case FILE_DEVICE_MULTIPLE_UNC_PROVIDER:
            }
#endif  /* NEVER */

            /* Update the user-supplied output value only after all has succeeded */
            /* BUGBUG - We should do that same for the FsInformation buffer */
            *FsAttributesLength = attr_len;
            NtClose(hand);
        }
        else
        {
            /* BUGBUG - Query by ordinal not supported */
            if (!Ordinal)
            {
                return ERROR_INVALID_PARAMETER;
            }
            return ERROR_INVALID_FUNCTION;
        }
    } except ( EXCEPTION_EXECUTE_HANDLER )
    {
       Od2ExitGP();
    }
    return NO_ERROR;
}


/* DosQueryFSAttach() */
/* Information level types (defines method of query) */
//#define FSAIL_QUERYNAME 1       /* Return data for a Drive or Device */
//#define FSAIL_DEVNUMBER 2       /* Return data for Ordinal Device # */
//#define FSAIL_DRVNUMBER 3       /* Return data for Ordinal Drive # */

/* Item types (from data structure item "iType") */
//#define FSAT_CHARDEV    1       /* Resident character device */
//#define FSAT_PSEUDODEV  2       /* Pseudo-character device */
//#define FSAT_LOCALDRV   3       /* Local drive */
//#define FSAT_REMOTEDRV  4       /* Remote drive attached to FSD */