/*++ Copyright (c) 1989 Microsoft Corporation Module Name: Version.c Abstract: This module implements a function to compare OS versions. Its the basis for VerifyVersionInfoW API. The Rtl version can be called from device drivers. Author: Nar Ganapathy [Narg] 19-Oct-1998 Environment: Pure utility routine Revision History: --*/ #include #include #if !defined(NTOS_KERNEL_RUNTIME) #include #endif #if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME) #pragma alloc_text(PAGE, RtlGetVersion) #endif // // The following comment explains the old and the new style layouts for the // condition masks. The condition mask is passed as a parameter to the // VerifyVersionInfo API. The condition mask encodes conditions like VER_AND, // VER_OR, VER_EQUAL for various types like VER_PLATFORMID, VER_MINORVERSION // etc., When the API was originally designed the application used a macro // called VER_SET_CONDTION which was defined to be _m_=(_m_|(_c_<<(1<<_t_))). // where _c_ is the condition and _t_ is the type. This macro is buggy for // types >= VER_PLATFORMID. Unfortunately a lot of application code already // uses this buggy macro (notably this terminal server) and have been shipped. // To fix this bug, a new API VerSetConditionMask is defined which has a new // bit layout. To provide backwards compatibility, we need to know if a // specific condition mask is a new style mask (has the new bit layout) or is // an old style mask. In both bit layouts bit 64 can never be set. // So the new API sets this bit to indicate that the condition mask is a new // style condition mask. So the code in this function that extracts the // condition uses the new bit layout if bit 63 is set and the old layout if // bit 63 is not set. This should allow applications that was compiled with // the old macro to work. // // // Use bit 63 to indicate that the new style bit layout is followed. // #define NEW_STYLE_BIT_MASK 0x8000000000000000 // // Condition extractor for the old style mask. // #define OLD_CONDITION(_m_,_t_) (ULONG)((_m_&(0xff<<(1<<_t_)))>>(1<<_t_)) // // Test to see if the mask is an old style mask. // #define OLD_STYLE_CONDITION_MASK(_m_) (((_m_) & NEW_STYLE_BIT_MASK) == 0) #define RTL_GET_CONDITION(_m_, _t_) \ (OLD_STYLE_CONDITION_MASK(_m_) ? (OLD_CONDITION(_m_,_t_)) : \ RtlpVerGetConditionMask((_m_), (_t_))) #define LEXICAL_COMPARISON 1 /* Do string comparison. Used for minor numbers */ #define MAX_STRING_LENGTH 20 /* Maximum number of digits for sprintf */ ULONG RtlpVerGetConditionMask( ULONGLONG ConditionMask, ULONG TypeMask ); /*++ Routine Description: This function retrieves the OS version information. Its the kernel equivalent of the GetVersionExW win 32 API. Arguments: lpVersionInformation - Supplies a pointer to the version info structure. In the kernel always assume that the structure is of type PRTL_OSVERSIONINFOEXW as its not exported to drivers. The signature is kept the same as for the user level RtlGetVersion. Return Value: Always succeeds and returns STATUS_SUCCESS. --*/ #if defined(NTOS_KERNEL_RUNTIME) NTSTATUS RtlGetVersion ( OUT PRTL_OSVERSIONINFOW lpVersionInformation ) { NT_PRODUCT_TYPE NtProductType; RTL_PAGED_CODE(); lpVersionInformation->dwMajorVersion = NtMajorVersion; lpVersionInformation->dwMinorVersion = NtMinorVersion; lpVersionInformation->dwBuildNumber = (USHORT)(NtBuildNumber & 0x3FFF); lpVersionInformation->dwPlatformId = 2; // VER_PLATFORM_WIN32_NT from winbase.h if (lpVersionInformation->dwOSVersionInfoSize == sizeof( RTL_OSVERSIONINFOEXW )) { ((PRTL_OSVERSIONINFOEXW)lpVersionInformation)->wServicePackMajor = ((USHORT)CmNtCSDVersion >> 8) & (0xFF); ((PRTL_OSVERSIONINFOEXW)lpVersionInformation)->wServicePackMinor = (USHORT)CmNtCSDVersion & 0xFF; ((PRTL_OSVERSIONINFOEXW)lpVersionInformation)->wSuiteMask = (USHORT)(USER_SHARED_DATA->SuiteMask&0xffff); ((PRTL_OSVERSIONINFOEXW)lpVersionInformation)->wProductType = (RtlGetNtProductType(&NtProductType) ? NtProductType :0); /* Not set as its not needed by VerifyVersionInfoW */ ((PRTL_OSVERSIONINFOEXW)lpVersionInformation)->wReserved = (UCHAR)0; } return STATUS_SUCCESS; } #else NTSTATUS RtlGetVersion( OUT PRTL_OSVERSIONINFOW lpVersionInformation ) { PPEB Peb; NT_PRODUCT_TYPE NtProductType; Peb = NtCurrentPeb(); lpVersionInformation->dwMajorVersion = Peb->OSMajorVersion; lpVersionInformation->dwMinorVersion = Peb->OSMinorVersion; lpVersionInformation->dwBuildNumber = Peb->OSBuildNumber; lpVersionInformation->dwPlatformId = Peb->OSPlatformId; if (Peb->CSDVersion.Buffer) { wcscpy( lpVersionInformation->szCSDVersion, Peb->CSDVersion.Buffer ); } else { lpVersionInformation->szCSDVersion[0] = 0; } if (lpVersionInformation->dwOSVersionInfoSize == sizeof( OSVERSIONINFOEXW )) { ((POSVERSIONINFOEXW)lpVersionInformation)->wServicePackMajor = (Peb->OSCSDVersion >> 8) & 0xFF; ((POSVERSIONINFOEXW)lpVersionInformation)->wServicePackMinor = Peb->OSCSDVersion & 0xFF; ((POSVERSIONINFOEXW)lpVersionInformation)->wSuiteMask = (USHORT)(USER_SHARED_DATA->SuiteMask&0xffff); ((POSVERSIONINFOEXW)lpVersionInformation)->wProductType = 0; if (RtlGetNtProductType( &NtProductType )) { ((POSVERSIONINFOEXW)lpVersionInformation)->wProductType = (UCHAR)NtProductType; if (NtProductType == VER_NT_WORKSTATION) { // // For workstation product never return VER_SUITE_TERMINAL // ((POSVERSIONINFOEXW)lpVersionInformation)->wSuiteMask = ((POSVERSIONINFOEXW)lpVersionInformation)->wSuiteMask & 0xffef; } } } return STATUS_SUCCESS; } #endif BOOLEAN RtlpVerCompare( LONG Condition, LONG Value1, LONG Value2, BOOLEAN *Equal, int Flags ) { char String1[MAX_STRING_LENGTH]; char String2[MAX_STRING_LENGTH]; LONG Comparison; if (Flags & LEXICAL_COMPARISON) { sprintf(String1, "%d", Value1); sprintf(String2, "%d", Value2); Comparison = strcmp(String2, String1); Value1 = 0; Value2 = Comparison; } *Equal = (Value1 == Value2); switch (Condition) { case VER_EQUAL: return (Value2 == Value1); case VER_GREATER: return (Value2 > Value1); case VER_LESS: return (Value2 < Value1); case VER_GREATER_EQUAL: return (Value2 >= Value1); case VER_LESS_EQUAL: return (Value2 <= Value1); default: break; } return FALSE; } NTSTATUS RtlVerifyVersionInfo( IN PRTL_OSVERSIONINFOEXW VersionInfo, IN ULONG TypeMask, IN ULONGLONG ConditionMask ) /*+++ This function verifies a version condition. Basically, this function lets an app query the system to see if the app is running on a specific version combination. Arguments: VersionInfo - a version structure containing the comparison data TypeMask - a mask comtaining the data types to look at ConditionMask - a mask containing conditionals for doing the comparisons Return Value: STATUS_INVALID_PARAMETER if the parameters are not valid. STATUS_REVISION_MISMATCH if the versions don't match. STATUS_SUCCESS if the versions match. --*/ { ULONG i; OSVERSIONINFOEXW CurrVersion; BOOLEAN SuiteFound = FALSE; BOOLEAN Equal; NTSTATUS Status; ULONG Condition; if (TypeMask == 0) { return STATUS_INVALID_PARAMETER; } RtlZeroMemory( &CurrVersion, sizeof(OSVERSIONINFOEXW) ); CurrVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); Status = RtlGetVersion((PRTL_OSVERSIONINFOW)&CurrVersion); if (Status != STATUS_SUCCESS) return Status; if ((TypeMask & VER_SUITENAME) && (VersionInfo->wSuiteMask != 0)) { for (i=0; i<16; i++) { if (VersionInfo->wSuiteMask&(1<dwMajorVersion, CurrVersion.dwMajorVersion, &Equal, 0 ) == FALSE) { if (!Equal) { return STATUS_REVISION_MISMATCH; } } } if (Equal) { ASSERT(Condition); if (TypeMask & VER_MINORVERSION) { if (Condition == VER_EQUAL) { Condition = RTL_GET_CONDITION(ConditionMask, VER_MINORVERSION); } if (RtlpVerCompare( Condition, VersionInfo->dwMinorVersion, CurrVersion.dwMinorVersion, &Equal, LEXICAL_COMPARISON ) == FALSE) { if (!Equal) { return STATUS_REVISION_MISMATCH; } } } if (Equal) { if (TypeMask & VER_SERVICEPACKMAJOR) { if (Condition == VER_EQUAL) { Condition = RTL_GET_CONDITION(ConditionMask, VER_SERVICEPACKMAJOR); } if (RtlpVerCompare( Condition, VersionInfo->wServicePackMajor, CurrVersion.wServicePackMajor, &Equal, 0 ) == FALSE) { if (!Equal) { return STATUS_REVISION_MISMATCH; } } } if (Equal) { if (TypeMask & VER_SERVICEPACKMINOR) { if (Condition == VER_EQUAL) { Condition = RTL_GET_CONDITION(ConditionMask, VER_SERVICEPACKMINOR); } if (RtlpVerCompare( Condition, (ULONG)VersionInfo->wServicePackMinor, (ULONG)CurrVersion.wServicePackMinor, &Equal, LEXICAL_COMPARISON ) == FALSE) { return STATUS_REVISION_MISMATCH; } } } } } if ((TypeMask & VER_BUILDNUMBER) && RtlpVerCompare( RTL_GET_CONDITION( ConditionMask, VER_BUILDNUMBER), VersionInfo->dwBuildNumber, CurrVersion.dwBuildNumber, &Equal, 0 ) == FALSE) { return STATUS_REVISION_MISMATCH; } if ((TypeMask & VER_PLATFORMID) && RtlpVerCompare( RTL_GET_CONDITION( ConditionMask, VER_PLATFORMID), VersionInfo->dwPlatformId, CurrVersion.dwPlatformId, &Equal, 0 ) == FALSE) { return STATUS_REVISION_MISMATCH; } if ((TypeMask & VER_PRODUCT_TYPE) && RtlpVerCompare( RTL_GET_CONDITION( ConditionMask, VER_PRODUCT_TYPE), VersionInfo->wProductType, CurrVersion.wProductType, &Equal, 0 ) == FALSE) { return STATUS_REVISION_MISMATCH; } return STATUS_SUCCESS; } ULONG RtlpVerGetConditionMask( ULONGLONG ConditionMask, ULONG TypeMask ) { ULONG NumBitsToShift; ULONG Condition = 0; if (!TypeMask) { return 0; } for (NumBitsToShift = 0; TypeMask; NumBitsToShift++) { TypeMask >>= 1; } Condition |= (ConditionMask) >> ((NumBitsToShift - 1) * VER_NUM_BITS_PER_CONDITION_MASK); Condition &= VER_CONDITION_MASK; return Condition; } ULONGLONG VerSetConditionMask( ULONGLONG ConditionMask, ULONG TypeMask, UCHAR Condition ) { int NumBitsToShift; Condition &= VER_CONDITION_MASK; if (!TypeMask) { return 0; } for (NumBitsToShift = 0; TypeMask; NumBitsToShift++) { TypeMask >>= 1; } // // Mark that we are using a new style condition mask // ConditionMask |= NEW_STYLE_BIT_MASK; ConditionMask |= (Condition) << ((NumBitsToShift - 1) * VER_NUM_BITS_PER_CONDITION_MASK); return ConditionMask; }