/*++ Copyright (c) 1998 Microsoft Corporation All rights reserved. Module Name: marshall.cxx Abstract: Code for custom marshalling spooler structures sent via RPC/LPC. It handles 32-64 bit machine compatibility depending on the route the call is comming. It can come either from Kernel mode (NATIVE_CALL), an in-proc winspool.drv call (NATIVE_CALL), a 32 bit process (RPC_CALL) or a 64 bit process (RPC_CALL). For native calls we perform basic marshalling. For RPC_CALLS we perform custom marshalling. Because there is no way to distinguish if a call came from a 64b or 32b proc, we always do custom marshalling across processes/wire. Author: Ramanathan Venkatapathy (RamanV) 4/30/98 Revision History: Adina Trufinescu (AdinaTru) 12/09/99 --*/ #include "spllibp.hxx" #pragma hdrstop #include "cstmarsh.h" /*++ Routine Name: GetShrinkedSize Routine Description: Calculates the size of a 64bit structure as it is on 32bit. Arguments: pFieldInfo -- structure containing information about fields inside the structure. pShrinkedSize -- how much difference it between the structure'ssize on 32bit and 64bit Return Value: Size of the 32bit structure. Last Error: Not set. --*/ EXTERN_C BOOL GetShrinkedSize( IN FieldInfo *pFieldInfo, OUT SIZE_T *pShrinkedSize ) { DWORD Index = 0; ULONG_PTR Size = 0; ULONG_PTR Alignment = 0; BOOL ReturnValue = FALSE; *pShrinkedSize = 0; // // For each field in the structure adds the length and enforce field's alignment. // For data fileds, the alignment is the same on both 32b and 64b. // for (Index = 0; pFieldInfo[Index].Offset != 0xffffffff; ++Index) { switch (pFieldInfo[Index].Type) { case PTR_TYPE: { // // Treat pointers as they are on 32bit. // Size = sizeof(DWORD32); Alignment = sizeof(DWORD32); break; } case DATA_TYPE: { Size = pFieldInfo[Index].Size; Alignment = pFieldInfo[Index].Alignment; break; } default: { SetLastError(ERROR_INVALID_PARAMETER); goto End; } } // // Enforce alignment before adding the size of the next field. // *pShrinkedSize = (SIZE_T)(AlignIt((PBYTE)*pShrinkedSize, Alignment)); // // Add field's size. // *pShrinkedSize += Size; } // // Almoust done. We need to align the 32b structure's size to 32bit since // structures come as an array. // Alignment = sizeof(DWORD32); *pShrinkedSize = (SIZE_T)(AlignIt((PBYTE)*pShrinkedSize, Alignment)); ReturnValue = TRUE; End: return ReturnValue; } /*++ Routine Name: MarshallDownStructure Routine Description: Marshalls down structures to be sent via RPC/LPC. Arguments: pStructure -- pointer to the structure to be marshalled down pFieldInfo -- structure containing information about fileds inside the structure StructureSize -- size of the unmarshalled structure RpcRoute -- indicates what type of marshalling we should do Return Value: TRUE if successful; Last Error: Set to ERROR_INVALID_PARAMETER if unknown Field type or architecture other than 32bit or 64bit. --*/ EXTERN_C BOOL MarshallDownStructure( IN OUT PBYTE pStructure, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN CALL_ROUTE Route ) { BOOL ReturnValue = FALSE; if (!pStructure || !pFieldInfo) { SetLastError(ERROR_INVALID_PARAMETER); goto End; } switch (kPointerSize) { case kSpl32Ptr: { // // 32 bit server does not require special marshalling; // ReturnValue = BasicMarshallDownStructure(pStructure, pFieldInfo); break; } case kSpl64Ptr : { switch (Route) { case NATIVE_CALL: { // // The call came from Kernel Mode. In KM the structure is basic marshalled. // We need to do the same thing // ReturnValue = BasicMarshallDownStructure(pStructure, pFieldInfo); break; } case RPC_CALL: { // // The call came through RPC. // Do the custom marshalling regardless of caller's bitness. // ReturnValue = CustomMarshallDownStructure(pStructure, pFieldInfo, StructureSize); break; } default: { // // Unknown route; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } break; } default: { // // Unknown pointer size; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } End: return ReturnValue; } /*++ Routine Name: MarshallDownEntry Routine Description: Custom marshalls down structures to be sent via RPC/LPC. Arguments: pStructure -- pointer to the structure to be marshalled down pNewStructure -- pointer to the new place where the structure will lay down in the array of marshalled down structures ( pStructure == pNewStructure on 32b) pFieldInfo -- structure containing information about fileds inside the structure StructureSize -- size of the unmarshalled structure RpcRoute -- indicates what type of marshalling we should do Return Value: TRUE if successful; Last Error: Set to ERROR_INVALID_PARAMETER if unknown Field type or architecture other than 32bit or 64bit. --*/ BOOL MarshallDownEntry( IN OUT PBYTE pStructure, IN PBYTE pNewStructure, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN CALL_ROUTE Route ) { BOOL ReturnValue = FALSE; if (!pStructure || !pFieldInfo) { SetLastError(ERROR_INVALID_PARAMETER); goto End; } switch (kPointerSize) { case kSpl32Ptr: { // // 32 bit server does not require special marshalling // ReturnValue = BasicMarshallDownEntry(pStructure, pFieldInfo); break; } case kSpl64Ptr : { switch (Route) { case NATIVE_CALL: { // // The call came from Kernel Mode. In KM the structure is basic marshalled. // We need to do the same thing here. // ReturnValue = BasicMarshallDownEntry(pStructure, pFieldInfo); break; } case RPC_CALL: { // // The call came through RPC. // Do the custom marshalling regardless of caller's bitness. // ReturnValue = CustomMarshallDownEntry(pStructure, pNewStructure, pFieldInfo, StructureSize); break; } default: { // // Unknown route; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } break; } default: { // // Unknown pointer size; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } End: return ReturnValue; } /*++ Routine Name: MarshallUpStructure Routine Description: Custom marshalls up structures to be sent via RPC/LPC. Arguments: pStructure -- pointer to the structure to be marshalled up pFieldInfo -- structure containing information about fileds inside the structure StructureSize -- size of the structure as it is to be when marsahlled up Route -- indicates what type of marshalling we should do Return Value: TRUE if successful. Last Error: Set to ERROR_INVALID_PARAMETER if unknown Field type or architecture other than 32bit or 64bit. --*/ EXTERN_C BOOL MarshallUpStructure( IN OUT PBYTE pStructure, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN CALL_ROUTE Route ) { BOOL ReturnValue = FALSE; if (!pStructure || !pFieldInfo) { SetLastError(ERROR_INVALID_PARAMETER); goto End; } switch (kPointerSize) { case kSpl32Ptr: { ReturnValue = BasicMarshallUpStructure(pStructure, pFieldInfo); break; } case kSpl64Ptr: { switch (Route) { case NATIVE_CALL: { // // The call came from Kernel Mode. In KM the structure is basic marshalled. // We need to do the same thing here. // ReturnValue = BasicMarshallUpStructure(pStructure, pFieldInfo); break; } case RPC_CALL: { // // The call came through RPC. // Do the custom marshalling regardless of caller's bitness. // ReturnValue = CustomMarshallUpStructure(pStructure, pFieldInfo, StructureSize); break; } default: { // // Unknown route; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } break; } default: { // // Unknown pointer size; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } End: return ReturnValue; } /*++ Routine Name: MarshallUpEntry Routine Description: Custom marshalls up structures to be sent via RPC/LPC. Arguments: pStructure -- pointer to the structure to be marshalled up pNewStructure -- pointer to the new place where the structure will lay down in the array of marshalled up structures ( pStructure == pNewStructure on 32b) pFieldInfo -- structure containing information about fileds inside the structure StructureSize -- size of the structure as it is to be when marshalled up Route -- determine what type of marshalling will be performed Return Value: TRUE if successful. Last Error: Set to ERROR_INVALID_PARAMETER if unknown Field type or architecture other than 32bit or 64bit. --*/ BOOL MarshallUpEntry( IN OUT PBYTE pStructure, IN PBYTE pNewStructure, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN SIZE_T ShrinkedSize, IN CALL_ROUTE Route ) { BOOL ReturnValue = FALSE; if (!pStructure || !pFieldInfo) { SetLastError(ERROR_INVALID_PARAMETER); goto End; } switch (kPointerSize) { case kSpl32Ptr: { ReturnValue = BasicMarshallUpEntry(pStructure, pFieldInfo); break; } case kSpl64Ptr : { switch (Route) { case NATIVE_CALL: { // // The call came from Kernel Mode. In KM the structure is basic marshalled. // We need to do the same thing here. // ReturnValue = BasicMarshallUpEntry(pStructure, pFieldInfo); break; } case RPC_CALL: { // // The call came through RPC. // Do the custom marshalling regardless of caller's bitness. // ReturnValue = CustomMarshallUpEntry(pStructure, pNewStructure, pFieldInfo, StructureSize, ShrinkedSize); break; } default: { // // Unknown route; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } break; } default: { // // Unknown pointer size; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } End: return ReturnValue; } /*++ Routine Name: MarshallDownStructuresArray Routine Description: Custom marshalls down array of structures to be sent via RPC/LPC. Arguments: pBufferArray -- pointer to the buffer containing the array of structures and packed data cReturned -- number of structures returned pFieldInfo -- structure containing information about fields inside the structure StructureSize -- size of the 64bit structure RpcRoute -- indicates what type of marshalling we should do Return Value: TRUE if successful. Last Error: Set to ERROR_INVALID_PARAMETER if unknown Field type or architecture other than 32bit or 64bit. --*/ EXTERN_C BOOL MarshallDownStructuresArray( IN OUT PBYTE pBufferArray, IN DWORD cReturned, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN CALL_ROUTE Route ) { DWORD Index = 0; PBYTE pStructure, pNewStructure; SIZE_T ShrinkedSize = 0; BOOL ReturnValue = FALSE; // // Check if there are any structures in the array. // This check must come before the one against pBufferArray and pFieldInfo. // If the Enum function didn't enumerate anything, we need to return success. // if (cReturned == 0) { ReturnValue = TRUE; goto End; } if (!pBufferArray || !pFieldInfo) { SetLastError(ERROR_INVALID_PARAMETER); goto End; } switch (kPointerSize) { case kSpl32Ptr: { // // The size of the structure remains the same on 32b. // ShrinkedSize = StructureSize; break; } case kSpl64Ptr: { switch (Route) { case NATIVE_CALL: { // // There is no need of special marshalling since the structures // need to stay padding unaltered. // ShrinkedSize = StructureSize; break; } case RPC_CALL: { // // Get the size of the 32b structure ; it takes care of both pointers and pointers/data alignments // if (!GetShrinkedSize(pFieldInfo, &ShrinkedSize)) { goto End; } break; } default: { // // Unknown route size; should never happen // SetLastError(ERROR_INVALID_PARAMETER); break; } } break; } default: { SetLastError(ERROR_INVALID_PARAMETER); goto End; } } // // pStructure is the pointer to the place where the 64b structure lays down in the array // pNewStructure is the pointer to the new place where the 32b structure will lay down in the array // MarshallDownEntry returns a pointer to the end of the just marshalled 32b structure which is // the new place where the next 32b marshalled structure will lay down in the array // for( Index = 0, pNewStructure = pStructure = pBufferArray; Index < cReturned ; Index++ , pStructure += StructureSize , pNewStructure += ShrinkedSize ) { if (!MarshallDownEntry(pStructure, pNewStructure, pFieldInfo, StructureSize, Route)) { goto End; } } ReturnValue = TRUE; End: return ReturnValue; } /*++ Routine Name: MarshallUpStructuresArray Routine Description: Custom marshalls up array of structures to be sent via RPC/LPC. Arguments: pBufferArray -- pointer to the buffer containing the array of structures and packed data cReturned -- number of structures returned pFieldInfo -- structure containing information about fileds inside the structure StructureSize -- size of the 64bit structure ( including the padding ) Route -- determine what type of marshalling will be performed Return Value: TRUE if successful. Last Error: Set to ERROR_INVALID_PARAMETER if unknown Field type or architecture other than 32bit or 64bit. --*/ EXTERN_C BOOL MarshallUpStructuresArray( IN OUT PBYTE pBufferArray, IN DWORD cReturned, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN CALL_ROUTE Route ) { INT32 Index = 0; PBYTE pStructure, pNextStructure; SIZE_T ShrinkedSize = 0; BOOL ReturnValue = FALSE; // // Check if there are any structures in the array. // This check must come before the one against pBufferArray and pFieldInfo. // If the Enum function didn't enumerate anything, we need to return success. // if (cReturned == 0) { ReturnValue = TRUE; goto End; } if (!pBufferArray || !pFieldInfo) { SetLastError(ERROR_INVALID_PARAMETER); goto End; } switch (kPointerSize) { case kSpl32Ptr: { // // The size of the structure remains the same on 32b. // ShrinkedSize = StructureSize; break; } case kSpl64Ptr: { // // Get the size of the 32b structure ; it takes care of both pointers and pointers/data alignments // if (!GetShrinkedSize(pFieldInfo, &ShrinkedSize)) { goto End; } break; } default: { SetLastError(ERROR_INVALID_PARAMETER); goto End; } } // // pBufferArray is an array of 32b stuctures; // pStructure is the pointer to the place where the 32b structure lays down in the array // pNewStructure is the pointer to the new place where the 64b structure will lay down in the array // for (Index = cReturned - 1; Index >= 0 ; Index--) { pStructure = pBufferArray + Index * ShrinkedSize; pNextStructure = pBufferArray + Index * StructureSize; if (!MarshallUpEntry(pStructure, pNextStructure, pFieldInfo, StructureSize, ShrinkedSize, Route)) { goto End; } } ReturnValue = TRUE; End: return ReturnValue; } /*++ Routine Name: UpdateBufferSize Routine Description: UpdateBufferSize adjusts the number of bytes required for returning the structures based on 32 and 64 bit clients and servers. Arguments: pOffsets - pointer to Offset struct cbStruct - sizeof struct cbStructAlign - sizeof struct aligned on 32b pcNeeded - pointer to number of bytes needed cbBuf - sizeof input buffer dwError - last error from RPC call pcReturned - pointer to number of returned structures (valid only if dwError == ERROR_SUCCESS) Return Value: Last Error; This function is called right after a RPC call. dwError is the return value of RPC call. The return value of this function is the result of applying of this code on the dwError. Last Error: Not set. --*/ EXTERN_C DWORD UpdateBufferSize( IN FieldInfo *pFieldInfo, IN SIZE_T cbStruct, IN OUT LPDWORD pcbNeeded, IN DWORD cbBuf, IN DWORD dwError, IN LPDWORD pcReturned ) { DWORD cStructures = 0; SIZE_T cbShrinkedStruct = 0; if (dwError != ERROR_SUCCESS && dwError != ERROR_MORE_DATA && dwError != ERROR_INSUFFICIENT_BUFFER) { // // RpcCall failed, no need to update required size // goto End; } if (!cbStruct) { dwError = ERROR_INVALID_PARAMETER; goto End; } switch (kPointerSize) { case kSpl32Ptr: { // // The pointers are not bigger on the server. Hence no adjustment is // required. // break; } case kSpl64Ptr: { if (!GetShrinkedSize(pFieldInfo, &cbShrinkedStruct)) { dwError = ERROR_INVALID_PARAMETER; goto End; } // // Increase the required size of buffer. This may be superfluous in the 64-64 // connection but this solution is simpler than adjusting pcbNeeded on the server. // // Count the number of structures to be returned // *pcbNeeded must be divided with the size of the structure on 32 bit. // cStructures = *pcbNeeded / (DWORD32)cbShrinkedStruct; // // For each structure, pcbNeeded is increased with the number of bites the pointers shrink // and the number of bites needed fpr padding // cbStruct - cbStructAlign is the number of bytes the compiler padds // *pcbNeeded += (DWORD) (cStructures * (cbStruct - cbShrinkedStruct)); if (cbBuf < *pcbNeeded && dwError == ERROR_SUCCESS) { dwError = ERROR_INSUFFICIENT_BUFFER; } break; } default: { // // Invalid pointer size; should not occur. // dwError = ERROR_INVALID_PARAMETER; break; } } End: return dwError; } /*++ Routine Name: AdjustPointers Routine Description: AdjustPointers adjusts pointer fields inside the structure. Arguments: pStructure -- pointer to a structructure pFieldInfo -- contains information about fields inside the structure cbAdjustment -- quantity to add to all pointer fields inside the structure Return Value: None. Last Error: Not set. --*/ EXTERN_C VOID AdjustPointers ( IN PBYTE pStructure, IN FieldInfo *pFieldInfo, IN ULONG_PTR cbAdjustment ) { PBYTE *pOffset = NULL; DWORD Index = 0; DWORD32 Offset = 0; for (Index = 0; Offset = pFieldInfo[Index].Offset, Offset != 0xffffffff; ++Index) { if (pFieldInfo[Index].Type == PTR_TYPE) { pOffset = (PBYTE *)(pStructure + Offset); if ( *pOffset ) { *pOffset += (ULONG_PTR)cbAdjustment; } } } } /*++ Routine Name: AdjustPointersInStructuresArray Routine Description: AdjustPointersInStructuresArray adjusts pointer fields inside the each structure of an array. Arguments: pBufferArray -- pointer to the buffer containing the array of structures cReturned -- number of structures in array pFieldInfo -- contains information about fields inside the structure StructureSize -- size of structure cbAdjustment -- quantity to add to all pointer fields inside the structure Return Value: None. Last Error: Not set. --*/ EXTERN_C VOID AdjustPointersInStructuresArray( IN PBYTE pBufferArray, IN DWORD cReturned, IN FieldInfo *pFieldInfo, IN SIZE_T StructureSize, IN ULONG_PTR cbAdjustment ) { INT32 Index = 0; PBYTE pStructure; if (cReturned && cbAdjustment && pBufferArray && pFieldInfo) { for (Index = cReturned - 1; Index >= 0 ; Index--) { pStructure = pBufferArray + Index * StructureSize; // // Call AdjustPointers for each entry in the array // AdjustPointers(pStructure, pFieldInfo, cbAdjustment); } } }