Windows2000/private/ntos/rtl/version.c
2020-09-30 17:12:32 +02:00

368 lines
12 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
--*/
#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 (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;
}
}
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 (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;
}