/*++

Copyright (c) 1987-1993  Microsoft Corporation

Module Name:

    ConvBlk.c

Abstract:

    This module contains RxpConvertBlock, a support routine for RxRemoteApi.

Author:

    John Rogers (JohnRo) 01-Apr-1991
        (Created portable LanMan (NT) version from LanMan 2.0)

Environment:

    Portable to any flat, 32-bit environment.  (Uses Win32 typedefs.)
    Requires ANSI C extensions: slash-slash comments, long external names.

Revision History:

    01-Apr-1991 JohnRo
        Converted from LanMan 2.x sources.
    03-May-1991 JohnRo
        Handle enum (array) correctly.
        Get 32-bit data desc and convert to receive buffer with it.
        Handle receive word correctly.
        Lots of cleanup to comments.
        RcvDataPointer and RcvDataPresent are redundant.
        Fixed receive buffer length problem.
        Added (quiet) debug output.
        Reduced recompile hits from header files.
    09-May-1991 JohnRo
        Made changes to reflect CliffV's code review.
    11-May-1991 JohnRo
        Convert pointers, and then tell convert single entry that input
        pointers are OK.  Also, let's treat Converter as DWORD locally.
        Force SmbGetUshort to get a word instead of a single byte.
    14-May-1991 JohnRo
        Pass 2 aux descriptors to RxpConvertBlock.
        Added debug print of NumStruct as it changes.
        Use FORMAT_LPVOID instead of FORMAT_POINTER (max portability).
    15-May-1991 JohnRo
        Added various "native" flags.
    17-May-1991 JohnRo
        Handle array of aux structs.
    20-May-1991 JohnRo
        Make data descriptors OPTIONAL for RxpConvertBlock.
    11-Jun-1991 rfirth
        Added extra parameter: SmbRcvByteLen which specifies the amount of
        bytes in SmbRcvBuffer
    14-Jun-1991 JohnRo
        Got rid of extraneous debug hex dump of buffer at end.
        Use NetpDbgReasonable().
    15-Jul-1991 JohnRo
        Changed RxpConvertDataStructures to allow ERROR_MORE_DATA, e.g. for
        print APIs.  Also got rid of a few unreferenced local variables.
    17-Jul-1991 JohnRo
        Extracted RxpDebug.h from Rxp.h.
    01-Aug-1991 RFirth
        Removed #if 0 block and variables which were put into convdata.c
        (RxpConvertDataStructures)
    19-Aug-1991 rfirth
        Added Flags parameter and support for ALLOCATE_RESPONSE flag
    26-Aug-1991 JohnRo
        Minor changes suggested by PC-LINT.
    20-Sep-1991 JohnRo
        Downlevel NetService APIs.  (Make sure *RcvDataBuffer gets set if
        ALLOCATE_RESPONSE is passed and *SmbRcvByteLen==0.)
    21-Nov-1991 JohnRo
        Removed NT dependencies to reduce recompiles.
    04-Nov-1992 JohnRo
        RAID 9355: Event viewer: won't focus on LM UNIX machine.
        (Added REM_DATA_BLOCK support for error log return data.)
        Use PREFIX_ equates.
    04-May-1993 JohnRo
        RAID 6167: avoid access violation or assert with WFW print server.
        Use NetpKdPrint() where possible.
    18-May-1993 JohnRo
        DosPrintQGetInfoW underestimates number of bytes needed.

--*/



// These must be included first:

#include <windef.h>             // IN, OUT, DWORD, LPBYTE, etc.
#include <lmcons.h>             // NET_API_STATUS.

// These may be included in any order:

#include <align.h>              // ALIGN_WORST
#include <apinums.h>            // API_ equates.
#include <limits.h>             // CHAR_BIT.
#include <lmapibuf.h>           // NetapipBufferAllocate, NetApiBufferFree
#include <lmerr.h>              // NERR_ and ERROR_ equates.
#include <netdebug.h>   // NetpAssert(), NetpDbg routines, FORMAT_ equates.
#include <netlib.h>             // NetpMoveMemory(), etc.
#include <prefix.h>     // PREFIX_ equates.
#include <remtypes.h>           // REM_BYTE, etc.
#include <rap.h>                // LPDESC, RapConvertSingleEntry(), etc.
#include <rx.h>                 // Flags parameter definitions
#include <rxp.h>                // My prototype.
#include <rxpdebug.h>           // IF_DEBUG().


#define DESC_IS_UNSTRUCTURED( descPtr ) \
    ( ( (descPtr)==NULL) || ( (*(descPtr)) == REM_DATA_BLOCK ) )

NET_API_STATUS
RxpConvertBlock(
    IN DWORD ApiNumber,
    IN LPBYTE ResponseBlockPointer,
    IN LPDESC ParmDescriptorString,
    IN LPDESC DataDescriptor16 OPTIONAL,
    IN LPDESC DataDescriptor32 OPTIONAL,
    IN LPDESC AuxDesc16 OPTIONAL,
    IN LPDESC AuxDesc32 OPTIONAL,
    IN va_list *FirstArgumentPointer,   // rest of API's arguments
    IN LPBYTE SmbRcvBuffer OPTIONAL,
    IN DWORD SmbRcvByteLen,
    OUT LPBYTE RcvDataBuffer OPTIONAL,
    IN DWORD RcvDataLength,
    IN DWORD Flags
    )

/*++

Routine Description:

    RxpConvertBlock translates the remote response (of a remoted API) into
    the local equivalent.  This involves converting the response (which is in
    the form of 16-bit data in the transaction response buffer) to local
    data formats, and setting them in the argument list.

Arguments:

    ApiNumber - Function number of the API required.

    ResponseBlockPointer - Points to the transaction SMB response block.

    ParmDescriptorString - A pointer to a ASCIIZ string describing the API call
        parameters (other than server name).

    DataDescriptor16 - A pointer to a ASCIIZ string describing the
        structure of the data in the call, i.e. the return data structure
        for a Enum or GetInfo call.  This string is used for adjusting pointers
        to data in the local buffers after transfer across the net.  If there
        is no structure involved in the call then DataDescriptor16 must
        be a null pointer.

    DataDescriptor32 - An optional pointer to a ASCIIZ string describing the
        32-bit structure of the return data structure.

    AuxDesc16, AuxDesc32 - Will be NULL in most cases unless a REM_AUX_COUNT
        descriptor char is present in DataDescriptor16 in which case the
        aux descriptors define a secondary data format as DataDescriptor16
        defines the primary.

    FirstArgumentPointer - Points to the va_list (variable arguments list)
        containing the API arguments (after the server name).  The caller
        must call va_start and va_end.

    SmbRcvBuffer - Optionally points to 16-bit format receive data buffer.

    SmbRcvByteLen - the number of bytes contained in SmbRcvBuffer (if not NULL)

    RcvDataBuffer - Points to the data area for the received data.  For
        instance, this may be a server info structure from NetServerGetInfo.
        This pointer will be NULL for many APIs.

        If (Flags & ALLOCATE_RESPONSE) then this pointer actually points to
        the pointer to the eventual buffer. We allocate a buffer in this routine
        and set *RcvDataBuffer to it. If we fail to get the buffer then
        *RcvDataBuffer will be set to NULL

    RcvDataLength - Length of the data area that RcvDataBuffer points to.
        If (Flags & ALLOCATE_RESPONSE) then this value will be the size that the
        caller of RxRemoteApi originally decided that the down-level server
        should use and incidentally was the original size of SmbRcvBuffer

    Flags - bit-mapped flags word. Currently defined flags are:
        NO_PERMISSION_REQUIRED  - used by RxpTransactSmb to determine whether
                                  a NULL session may be used
        ALLOCATE_RESPONSE       - used by this routine to allocate the final
                                  32-bit response data buffer based on the size
                                  of the SMB data received, multiplied by the
                                  RAP_CONVERSION_FACTOR
Return Value:

    NET_API_STATUS - return value from remote API.

--*/

{
    DWORD Converter;            // For pointer fixups.
    LPBYTE CurrentBlockPointer;
    LPDWORD        EntriesReadPtr = NULL;
    DWORD NumStruct;            // Loop count for ptr fixup.
    va_list ParmPtr;
    LPBYTE         pDataBuffer = NULL;  // pointer to returned data
    NET_API_STATUS Status;      // Return status from remote.
    DWORD TempLength;           // General purpose length.


#if DBG

    //
    // Code in this file depends on 16-bit words; the Remote Admin Protocol
    // demands it.
    //

    NetpAssert( ( (sizeof(WORD)) * CHAR_BIT) == 16);

    if (DataDescriptor16 != NULL) {
        NetpAssert(DataDescriptor32 != NULL);
    } else {
        NetpAssert(DataDescriptor32 == NULL);
    }

    if (AuxDesc16 != NULL) {
        NetpAssert(AuxDesc32 != NULL);
    } else {
        NetpAssert(AuxDesc32 == NULL);
    }
#endif


    ParmPtr = *FirstArgumentPointer;

    // The API call was successful. Now translate the return buffers
    // into the local API format.
    //
    // First copy any data from the return parameter buffer into the
    // fields pointed to by the original call parameters.
    // The return parameter buffer contains;
    //      Status,         (16 bits)
    //      Converter,      (16 bits)
    //      ...             fields described by rcv ptr types in
    //                      ParmDescriptorString


    CurrentBlockPointer = ResponseBlockPointer;
    Status = (NET_API_STATUS) SmbGetUshort( (LPWORD) CurrentBlockPointer );
    CurrentBlockPointer += sizeof(WORD);

    Converter = ((DWORD) SmbGetUshort( (LPWORD) CurrentBlockPointer )) & 0xffff;
    IF_DEBUG(CONVBLK) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpConvertBlock: Converter=" FORMAT_HEX_DWORD ".\n",
                Converter ));
    }
    CurrentBlockPointer += sizeof(WORD);

    // Set default value of NumStruct to 1, if data, 0 if no data.

    if ( (DataDescriptor16 != NULL) && (*DataDescriptor16 != '\0') ) {
        NumStruct = 1;
    } else {
        NumStruct = 0;
    }

    for( ; *ParmDescriptorString != '\0'; ParmDescriptorString++) {

        IF_DEBUG(CONVBLK) {
            NetpKdPrint(( PREFIX_NETAPI
                    "RxpConvertBlock: *parm='" FORMAT_LPDESC_CHAR
                    "', ParmPtr is:\n", *ParmDescriptorString ));
            NetpDbgHexDump((LPVOID) & ParmPtr, sizeof(va_list));
        }
        switch( *ParmDescriptorString) {
        case REM_WORD :                 // Word in old APIs (DWORD in 32-bit).
        case REM_DWORD :                // DWord.
            // BUGBUG: is array possible here?
            (void) va_arg(ParmPtr, DWORD);      // Step over this arg.
            break;

        case REM_ASCIZ :
            (void) va_arg(ParmPtr, LPSTR);      // Step over this arg.
            break;

        case REM_BYTE_PTR :
            (void) va_arg(ParmPtr, LPBYTE);     // Step over this arg.
            (void) RapArrayLength(
                        ParmDescriptorString,
                        &ParmDescriptorString,
                        Response);
            break;

        case REM_WORD_PTR :             // (WORD *) in old APIs.
        case REM_DWORD_PTR :            // (DWORD *)
            (void) va_arg(ParmPtr, LPDWORD);    // Step over this arg.
            break;

        case REM_RCV_WORD_PTR :    // pointer to rcv word(s) (DWORD in 32-bit)
            {
                LPDWORD Temp;
                DWORD ArrayCount;
                Temp = va_arg(ParmPtr, LPDWORD);

                ++ParmDescriptorString;  // point to first (possible) digit...
                ArrayCount = RapDescArrayLength(
                        ParmDescriptorString);  // updated past last.
                --ParmDescriptorString;  // point back at last digit for loop.
                IF_DEBUG(CONVBLK) {
                    NetpKdPrint(( PREFIX_NETAPI
                            "RxpConvertBlock: rcv.word.ptr, temp="
                            FORMAT_LPVOID ", ArrayCount=" FORMAT_DWORD ".\n",
                            (LPVOID) Temp, ArrayCount ));
                }

                // if the rcv buffer given to us by the user is NULL,
                // (one currently can be - it is an MBZ parameter for
                // now in the log read apis...), don't attempt to
                // copy anything. TempLength will be garbage in this
                // case, so don't update CurrentBlockPointer either.  All we
                // use RapArrayLength for is to update ParmDescriptorString if
                // the parameter was NULL.

                if ( Temp == NULL ) {
                    ;        /* NULLBODY */
                } else {

                    // Copy one or more words (expanding to DWORDS as we go).
                    DWORD WordsLeft = ArrayCount;
                    do {
                        DWORD Data;
                        // Convert byte order if necessary, and expand.
                        Data = (DWORD) SmbGetUshort(
                                (LPWORD) CurrentBlockPointer );
                        *Temp = Data;
                        Temp += sizeof(DWORD);
                        --WordsLeft;
                    } while (WordsLeft > 0);

                    // This gross hack is to fix the problem that a
                    // down level spooler (Lan Server 1.2)
                    // do not perform level checking
                    // on the w functions of the api(s):
                    // DosPrintQGetInfo
                    // and thus can return NERR_Success
                    // and bytesavail == 0.  This combination
                    // is technically illegal, and results in
                    // us attempting to unpack a buffer full of
                    // garbage.  The following code detects this
                    // condition and resets the amount of returned
                    // data to zero so we do not attempt to unpack
                    // the buffer.        Since we know the reason for the
                    // mistake at the server end is that we passed
                    // them a new level, we return ERROR_INVALID_LEVEL
                    // in this case.
                    // ERICPE, 5/16/90.

                    if ((ApiNumber == API_WPrintQGetInfo)
                    && (Status == NERR_Success)
                    && (*(LPWORD)CurrentBlockPointer == 0)
                    && (*ParmDescriptorString == REM_RCV_WORD_PTR)) {

                        Status = ERROR_INVALID_LEVEL;
                        goto ErrorExit;
                    }
                    // END OF GROSS HACK

                    CurrentBlockPointer += (ArrayCount * sizeof(WORD));
                 }
                break;
            }

        case REM_RCV_BYTE_PTR :         // pointer to rcv byte(s)
            {
                LPBYTE Temp;
                Temp = va_arg(ParmPtr, LPBYTE);
                TempLength = RapArrayLength(
                        ParmDescriptorString,
                        &ParmDescriptorString,
                        Response);

                // if the rcv buffer given to us by the user is NULL,
                // (one currently can be - it is an MBZ parameter for
                // now in the log read apis...), don't attempt to
                // copy anything. TempLength will be garbage in this
                // case, so don't update CurrentBlockPointer either.  All we
                // use RapArrayLength for is to update ParmDescriptorString if
                // the parameter was NULL.

                if ( Temp != NULL ) {
                    NetpMoveMemory(
                                Temp,                           // dest
                                CurrentBlockPointer,            // src
                                TempLength);                    // len
                    CurrentBlockPointer += TempLength;
                 }
            }
            break;

        case REM_RCV_DWORD_PTR :        // pointer to rcv Dword(s)
            {
                LPDWORD Temp;
                Temp = va_arg(ParmPtr, LPDWORD);
                TempLength = RapArrayLength(
                        ParmDescriptorString,
                        &ParmDescriptorString,
                        Response);

                // if the rcv buffer given to us by the user is NULL,
                // (one currently can be - it is an MBZ parameter for
                // now in the log read apis...), don't attempt to
                // copy anything. TempLength will be garbage in this
                // case, so don't update CurrentBlockPointer either.  All we
                // use RapArrayLength for is to update ParmDescriptorString if
                // the parameter was NULL.

                if ( Temp == NULL ) {
                    ;        /* NULLBODY */
                } else {
                    // BUGBUG: Must byte swap here if necessary!
                    NetpMoveMemory(
                                Temp,                           // dest
                                CurrentBlockPointer,            // src
                                TempLength);                    // len
                    CurrentBlockPointer += TempLength;
                }
            }
            break;

        case REM_SEND_BUF_PTR :
            (void) va_arg(ParmPtr, LPVOID);     // Step over arg.
            break;

        case REM_SEND_BUF_LEN :
            (void) va_arg(ParmPtr, DWORD);      // Step over (32-bit) buf len.
            break;

        case REM_RCV_BUF_PTR :
            (void) va_arg(ParmPtr, LPVOID);
            break;

        case REM_RCV_BUF_LEN :
            (void) va_arg(ParmPtr, DWORD);      // Step over (32-bit) buf len.
            break;

        case REM_PARMNUM :
            (void) va_arg(ParmPtr, DWORD);      // Step over (32-bit) parm num.
            break;

        case REM_ENTRIES_READ :          // Used for NumStruct
            {
                EntriesReadPtr = va_arg(ParmPtr, LPDWORD);
                NumStruct = (DWORD) SmbGetUshort((LPWORD) CurrentBlockPointer);
                // BUGBUG: Is this appropriate way to handle truncation?
                NetpAssert(! RapValueWouldBeTruncated(NumStruct));
                IF_DEBUG(CONVBLK) {
                    NetpKdPrint(( PREFIX_NETAPI
                            "RxpConvertBlock: NumStruct is now "
                            FORMAT_DWORD ".\n", NumStruct ));
                }

                // Assume all entries will fit; we'll correct this later if not.
                *EntriesReadPtr = NumStruct;

                CurrentBlockPointer += sizeof(WORD);
                break;
            }

        case REM_FILL_BYTES :
            // Special case, this was not really an input parameter so ParmPtr
            // does not get changed. However, the ParmDescriptorString
            // pointer must be advanced past the descriptor field so
            // use get RapArrayLength to do this but ignore the
            // return length.

            (void) RapArrayLength(
                        ParmDescriptorString,
                        &ParmDescriptorString,
                        Response);
            break;

        case REM_AUX_NUM :              // Can't have aux in parm desc.
        case REM_BYTE :                 // Can't push a byte, so this is bogus?
        case REM_DATA_BLOCK :           // BUGBUG: Not in parm desc?
        case REM_DATE_TIME :            // Never used?
        case REM_NULL_PTR :             // BUGBUG: Not sure what to do here.
        case REM_SEND_LENBUF :          // BUGBUG: Not sure what to do here.
        default :
            NetpBreakPoint();
            Status = NERR_InternalError;
            goto ErrorExit;

        } // switch
    } // for

    //
    // If no data was returned from the server, then there's no point in
    // continuing. Return early with the status code as returned from the
    // remoted function
    //

    if (!SmbRcvByteLen) {
        if (Flags & ALLOCATE_RESPONSE) {

            //
            // We failed to allocate the buffer; this in turn will cause
            // RxRemoteApi to fail, in which event, the calling function
            // (ie RxNetXxxx) may try to free the buffer allocated on its
            // behalf (ie the buffer we just failed to get). Ensure that
            // the caller doesn't try to free an invalid pointer by setting
            // the returned pointer to NULL
            //

            NetpAssert(RcvDataBuffer);  // address of the callers buffer pointer
            *(LPBYTE*)RcvDataBuffer = NULL;
        }

        return Status;
    }

    //
    // If the caller of RxRemoteApi requested that we allocate the final data
    // buffer on their behalf, then allocate it here. We use as the size
    // criterion
    //
    // (RAP_CONVERSION_FACTOR + 1/RAP_CONVERSION_FRACTION) * SmbRcvByteLen
    //
    // since this has the size of 16-bit data actually received.
    //
    // RAP_CONVERSION_FACTOR is 2 since that's the ratio for the size of
    // WCHAR to CHAR and of DWORD to WORD.  However, Lanman data structures
    // typically represents text as zero terminated array of CHAR within the
    // returned structure.  The array itself is of maximum size.  However,
    // the typical native representation has a 4-byte pointer to a zero
    // terminated WCHAR.  A factor of 2 wouldn't account for the 4-byte pointer.
    // Assuming the smallest lanman array size is 13 bytes (e.g., NNLEN+1), an
    // additional factor of 4/13 (about 1/3) is needed.  So,
    // RAP_CONVERSION_FRACTION is 3.
    //
    // Round the size to a multiple of the alignment for the system to allow
    // data to be packed at the trailing end of the buffer.
    //
    // NOTE: Since the original API caller expects to use NetApiBufferFree to
    // get rid of this buffer, we must use NetapipBufferAllocate
    //

    if (Flags & ALLOCATE_RESPONSE) {
        NET_API_STATUS  ConvertStatus;

        NetpAssert(RcvDataBuffer);  // address of the callers buffer pointer
        NetpAssert(SmbRcvByteLen);  // size of the data received

        RcvDataLength = SmbRcvByteLen * RAP_CONVERSION_FACTOR;
        RcvDataLength += (SmbRcvByteLen + RAP_CONVERSION_FRACTION - 1) /
                         RAP_CONVERSION_FRACTION;
        RcvDataLength = ROUND_UP_COUNT( RcvDataLength, ALIGN_WORST );

        if (ConvertStatus = NetapipBufferAllocate(RcvDataLength,
                                                  (PVOID *) &pDataBuffer)) {
            NetpKdPrint(( PREFIX_NETAPI
                    "Error: RxpConvertBlock cannot allocate memory ("
                    "error " FORMAT_API_STATUS ").\n", ConvertStatus ));

            Status = ConvertStatus;
            goto ErrorExit;
        }
        NetpAssert( pDataBuffer != NULL );

        IF_DEBUG(CONVBLK) {
            NetpKdPrint(( PREFIX_NETAPI
                    "RxpConvertBlock: allocated " FORMAT_DWORD " byte buffer "
                    "at " FORMAT_LPVOID " for caller\n",
                    RcvDataLength, (LPVOID) pDataBuffer ));
        }

        *(LPBYTE*)RcvDataBuffer = pDataBuffer;
    } else {
        pDataBuffer = RcvDataBuffer;
    }

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    // NB:  From here on down, RcvDataBuffer should not be used to point to //
    //      the received data buffer. Use pDataBuffer. The reason is that   //
    //      RcvDataBuffer is ambiguous - it may point to a buffer or it may //
    //      point to a pointer to a buffer. pDataBuffer will always point   //
    //      to a buffer                                                     //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    //
    // Done doing arguments, so now we can do the receive buffer.
    //

    if ((pDataBuffer != NULL) && (RcvDataLength != 0)) {
        // DWORD BytesRequired = 0;
        LPBYTE EntryPtr16 = SmbRcvBuffer;
        // LPBYTE EntryPtr32 = pDataBuffer;
        DWORD NumAuxStructs;
        // LPBYTE StringAreaEnd = pDataBuffer + RcvDataLength;
        // BOOL    auxiliaries = AuxDesc32 != NULL;

//
// MOD 06/06/91 RLF
//
//        NetpAssert( DataDescriptor16 != NULL );
//
// MOD 06/06/91 RLF
//
        IF_DEBUG(CONVBLK) {
            NetpKdPrint(( PREFIX_NETAPI
                    "RxpConvertBlock: "
                    "SMB rcv buff (before rcv buff conv) (partial):\n" ));
            // BUGBUG: wrong len below?
            NetpDbgHexDump(SmbRcvBuffer, NetpDbgReasonable(RcvDataLength));
        }

        // Now convert all pointer fields in the receive buffer to local
        // pointers.

//
// MOD 06/06/91 RLF
//
// If we have a receive data buffer, we may or may not be receiving
// structured data. If DataDescriptor16 is NULL assume unstructured data
// else work out sizes of structs etc.
//
// BUGBUG?
// When I say "unstructured data", is it okay to treat this as an array of
// bytes? What about ASCIZ-UC translation? My guess is that at this level
// unstructured data is bytes and any higher-level software which knows
// the format of received/expected data can process it
//
        if ( !DESC_IS_UNSTRUCTURED( DataDescriptor16 ) ) {

            NET_API_STATUS ConvertStatus;
            DWORD          NumCopied = 0;
//
// MOD 06/06/91 RLF
//
            ConvertStatus = RxpReceiveBufferConvert(
                    SmbRcvBuffer,               // buffer (updated in place)
                    RcvDataLength,              // buffer size in bytes
                    Converter,
                    NumStruct,
                    DataDescriptor16,
                    AuxDesc16,
                    & NumAuxStructs);
            if (ConvertStatus != NERR_Success) {
                Status = ConvertStatus;
                goto ErrorExit;
            }

            ConvertStatus = RxpConvertDataStructures(
                DataDescriptor16,   // going from 16-bit data
                DataDescriptor32,   // to 32-bit
                AuxDesc16,          // same applies for aux data
                AuxDesc32,
                EntryPtr16,         // where the 16-bit data is
                pDataBuffer,        // where the 32-bit data goes
                RcvDataLength,      // size of the output buffer (bytes)
                NumStruct,          // number of primaries
                &NumCopied,         // number of primaries copied.
                Both,               // parameter to RapConvertSingleEntry
                RapToNative         // convert 16-bit data to 32-bit
                );

            if (ConvertStatus != NERR_Success) {

                //
                // This only happens when (1) the API allows the application
                // to specify the buffer size and (2) the size is too small.
                // As part of the "switch" above, we've already set the
                // "pcbNeeded" (pointer to count of bytes needed).  Actually,
                // that value assumes that the RAP and native sizes are the
                // same.  It's up to RxRemoteApi's caller to correct that,
                // if it's even feasible.
                //

                //
                // BUGBUG: Anything else to do here?
                //

                NetpAssert( ConvertStatus == ERROR_MORE_DATA );

                if (EntriesReadPtr != NULL) {
                    // Some APIs, like DosPrintQEnum, have entries read value.
                    NetpAssert( NumCopied <= NumStruct );
                    *EntriesReadPtr = NumCopied;
                } else {
                    // APIs like DosPrintQGetInfo does not have entries read.
                    // There isn't much we can do for them.
                }

                Status = ConvertStatus;
                // Continue, treating this as "normal" status...

            } else {
                NetpAssert( NumCopied == NumStruct );
            }

            IF_DEBUG(CONVBLK) {
                NetpKdPrint(( PREFIX_NETAPI
                        "RxpConvertBlock: rcv buff (after CSE) (partial):\n" ));
                NetpDbgHexDump(pDataBuffer, NetpDbgReasonable(RcvDataLength));
            }

        // BUGBUG: Eventually compress out unused space (BytesRequired vs.
        // RcvDataLength).

        } else {

            //
            // There is no 16-bit data descriptor. We take this to mean that
            // the data is unstructured - typically a string will be returned
            // An example? Why, I_NetNameCanonicalize of course!
            //
#if DBG
            //
            // BUGBUG - Does this check need to go in release code too?
            //

            NetpAssert(RcvDataLength >= SmbRcvByteLen);
#endif

            //
            // BUGBUG - this is not sufficient - need to convert ASCII-UNICODE??
            //          other data conversions? How specified?
            //

            NetpMoveMemory(pDataBuffer, SmbRcvBuffer, SmbRcvByteLen);
        }
    } // if pDataBuffer && RcvDataLength

    IF_DEBUG(CONVBLK) {
        NetpKdPrint(( PREFIX_NETAPI
                "RxpConvertBlock: returning (normal) status="
                FORMAT_API_STATUS "\n", Status ));
    }

    return(Status);

ErrorExit:

    NetpAssert( Status != NO_ERROR );

    // BUGBUG: eventlog this.

    if (Flags & ALLOCATE_RESPONSE) {
        //
        // If we already allocated a buffer on behalf of the caller of
        // RxRemoteApi, then free it up.
        //

        if (pDataBuffer != NULL) {
            (VOID) NetApiBufferFree(pDataBuffer);
        }

        //
        // We failed to allocate the buffer; this in turn will cause
        // RxRemoteApi to fail, in which event, the calling function
        // (ie RxNetXxxx) may try to free the buffer allocated on its
        // behalf (ie the buffer we just failed to get).  Ensure that
        // the caller doesn't try to free an invalid pointer by setting
        // the returned pointer to NULL.
        //

        NetpAssert( RcvDataBuffer != NULL );
        *(LPBYTE*)RcvDataBuffer = NULL;
    }

    NetpKdPrint(( PREFIX_NETAPI
            "RxpConvertBlock: returning error status="
            FORMAT_API_STATUS "\n", Status ));

    return (Status);

} // RxpConvertBlock
