windows-nt/Source/XPSP1/NT/base/ntos/rtl/version.c
2020-09-26 16:20:57 +08:00

461 lines
13 KiB
C

/*++
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 <stdio.h>
#include <ntrtlp.h>
#if !defined(NTOS_KERNEL_RUNTIME)
#include <winerror.h>
#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<<i)) {
switch (RTL_GET_CONDITION(ConditionMask,VER_SUITENAME)) {
case VER_AND:
if (!(CurrVersion.wSuiteMask&(1<<i))) {
return STATUS_REVISION_MISMATCH;
}
break;
case VER_OR:
if (CurrVersion.wSuiteMask&(1<<i)) {
SuiteFound = TRUE;
}
break;
default:
return STATUS_INVALID_PARAMETER;
}
}
}
if ((RtlpVerGetConditionMask(ConditionMask,VER_SUITENAME) == VER_OR) && (SuiteFound == FALSE)) {
return STATUS_REVISION_MISMATCH;
}
}
Equal = TRUE;
Condition = VER_EQUAL;
if (TypeMask & VER_MAJORVERSION) {
Condition = RTL_GET_CONDITION( ConditionMask, VER_MAJORVERSION);
if (RtlpVerCompare(
Condition,
VersionInfo->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;
}