Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1411 lines
46 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
NtfsInit.c
Abstract:
This module implements the DRIVER_INITIALIZATION routine for Ntfs
Author:
Gary Kimura [GaryKi] 21-May-1991
Revision History:
--*/
#include "NtfsProc.h"
#define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
//
// Reference our local attribute definitions
//
extern ATTRIBUTE_DEFINITION_COLUMNS NtfsAttributeDefinitions[];
NTSTATUS
DriverEntry (
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
VOID
NtfsInitializeNtfsData (
IN PDRIVER_OBJECT DriverObject,
IN USHORT MemoryMultiplier
);
NTSTATUS
NtfsQueryValueKey (
IN PUNICODE_STRING KeyName,
IN PUNICODE_STRING ValueName,
IN OUT PULONG ValueLength,
IN OUT PKEY_VALUE_FULL_INFORMATION *KeyValueInformation,
IN OUT PBOOLEAN DeallocateKeyValue
);
BOOLEAN
NtfsRunningOnWhat(
IN USHORT SuiteMask,
IN UCHAR ProductType
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(INIT, NtfsInitializeNtfsData)
#pragma alloc_text(INIT, NtfsQueryValueKey)
#pragma alloc_text(INIT, NtfsRunningOnWhat)
#endif
#define UPGRADE_SETUPDD_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Setupdd"
#define UPGRADE_SETUPDD_VALUE_NAME L"Start"
#define UPGRADE_CHECK_SETUP_KEY_NAME L"\\Registry\\Machine\\System\\Setup"
#define UPGRADE_CHECK_SETUP_VALUE_NAME L"SystemSetupInProgress"
#define UPGRADE_CHECK_SETUP_CMDLINE_NAME L"CmdLine"
#define UPGRADE_CHECK_SETUP_ASR L"-asr"
#define UPGRADE_CHECK_SETUP_NEWSETUP L"-newsetup"
#define COMPATIBILITY_MODE_KEY_NAME L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\FileSystem"
#define COMPATIBILITY_MODE_VALUE_NAME L"NtfsDisable8dot3NameCreation"
#define EXTENDED_CHAR_MODE_VALUE_NAME L"NtfsAllowExtendedCharacterIn8dot3Name"
#define DISABLE_LAST_ACCESS_VALUE_NAME L"NtfsDisableLastAccessUpdate"
#define QUOTA_NOTIFY_RATE L"NtfsQuotaNotifyRate"
#define MFT_ZONE_SIZE_VALUE_NAME L"NtfsMftZoneReservation"
#define MEMORY_USAGE_VALUE_NAME L"NtfsMemoryUsage"
#define KEY_WORK_AREA ((sizeof(KEY_VALUE_FULL_INFORMATION) + \
sizeof(ULONG)) + 128)
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
/*++
Routine Description:
This is the initialization routine for the Ntfs file system
device driver. This routine creates the device object for the FileSystem
device and performs all other driver initialization.
Arguments:
DriverObject - Pointer to driver object created by the system.
Return Value:
NTSTATUS - The function value is the final status from the initialization
operation.
--*/
{
NTSTATUS Status;
UNICODE_STRING UnicodeString;
PDEVICE_OBJECT DeviceObject;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
ULONG Value;
ULONG KeyValueLength;
UCHAR Buffer[KEY_WORK_AREA];
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
BOOLEAN DeallocateKeyValue;
ULONG MemoryMultiplier;
USHORT NtfsDataFlags = 0;
UNREFERENCED_PARAMETER( RegistryPath );
PAGED_CODE();
//
// Check to make sure structure overlays are correct.
//
ASSERT( FIELD_OFFSET( FILE_NAME, ParentDirectory) == FIELD_OFFSET(OVERLAY_LCB, OverlayParentDirectory ));
ASSERT( FIELD_OFFSET( FILE_NAME, FileNameLength) == FIELD_OFFSET(OVERLAY_LCB, OverlayFileNameLength ));
ASSERT( FIELD_OFFSET( FILE_NAME, Flags) == FIELD_OFFSET(OVERLAY_LCB, OverlayFlags ));
ASSERT( FIELD_OFFSET( FILE_NAME, FileName) == FIELD_OFFSET(OVERLAY_LCB, OverlayFileName ));
ASSERT( sizeof( DUPLICATED_INFORMATION ) >= (sizeof( QUICK_INDEX ) + (sizeof( ULONG ) * 4) + sizeof( PFILE_NAME )));
//
// The open attribute table entries should be 64-bit aligned.
//
ASSERT( sizeof( OPEN_ATTRIBUTE_ENTRY ) == QuadAlign( sizeof( OPEN_ATTRIBUTE_ENTRY )));
//
// The first entry in an open attribute data should be the links.
//
ASSERT( FIELD_OFFSET( OPEN_ATTRIBUTE_DATA, Links ) == 0 );
//
// Compute the last access increment. We convert the number of
// minutes to number of 1/100 of nanoseconds. We have to be careful
// not to overrun 32 bits for any multiplier.
//
// To reach 1/100 of nanoseconds per minute we take
//
// 1/100 nanoseconds * 10 = 1 microsecond
// * 1000 = 1 millesecond
// * 1000 = 1 second
// * 60 = 1 minute
//
// Then multiply this by the last access increment in minutes.
//
NtfsLastAccess = Int32x32To64( ( 10 * 1000 * 1000 * 60 ), LAST_ACCESS_INCREMENT_MINUTES );
//
// Allocate the reserved buffers for USA writes - do this early so we don't have any
// teardown to do.
//
NtfsReserved1 = NtfsAllocatePoolNoRaise( NonPagedPool, LARGE_BUFFER_SIZE );
if (NULL == NtfsReserved1) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Buffer 2 is used for the workspace. It may require a slightly larger buffer on
// a Win64 system.
//
NtfsReserved2 = NtfsAllocatePoolNoRaise( NonPagedPool, WORKSPACE_BUFFER_SIZE );
if (NULL == NtfsReserved2) {
NtfsFreePool( NtfsReserved1 );
return STATUS_INSUFFICIENT_RESOURCES;
}
NtfsReserved3 = NtfsAllocatePoolNoRaise( NonPagedPool, LARGE_BUFFER_SIZE );
if (NULL == NtfsReserved3) {
NtfsFreePool( NtfsReserved1 );
NtfsFreePool( NtfsReserved2 );
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Create the device object.
//
RtlInitUnicodeString( &UnicodeString, L"\\Ntfs" );
Status = IoCreateDevice( DriverObject,
0,
&UnicodeString,
FILE_DEVICE_DISK_FILE_SYSTEM,
0,
FALSE,
&DeviceObject );
if (!NT_SUCCESS( Status )) {
return Status;
}
//
// Note that because of the way data caching is done, we set neither
// the Direct I/O or Buffered I/O bit in DeviceObject->Flags. If
// data is not in the cache, or the request is not buffered, we may,
// set up for Direct I/O by hand.
//
//
// Initialize the driver object with this driver's entry points.
//
DriverObject->MajorFunction[IRP_MJ_QUERY_EA] =
DriverObject->MajorFunction[IRP_MJ_SET_EA] =
DriverObject->MajorFunction[IRP_MJ_QUERY_QUOTA] =
DriverObject->MajorFunction[IRP_MJ_SET_QUOTA] =
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)NtfsFsdDispatchWait;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
DriverObject->MajorFunction[IRP_MJ_QUERY_SECURITY] =
DriverObject->MajorFunction[IRP_MJ_SET_SECURITY] =
DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] =
DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)NtfsFsdDispatch;
DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)NtfsFsdLockControl;
DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)NtfsFsdDirectoryControl;
DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)NtfsFsdSetInformation;
DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)NtfsFsdCreate;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)NtfsFsdClose;
DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)NtfsFsdRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)NtfsFsdWrite;
DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)NtfsFsdFlushBuffers;
DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)NtfsFsdFileSystemControl;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)NtfsFsdCleanup;
DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)NtfsFsdShutdown;
DriverObject->MajorFunction[IRP_MJ_PNP] = (PDRIVER_DISPATCH)NtfsFsdPnp;
DriverObject->FastIoDispatch = &NtfsFastIoDispatch;
NtfsFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
NtfsFastIoDispatch.FastIoCheckIfPossible = NtfsFastIoCheckIfPossible; // CheckForFastIo
NtfsFastIoDispatch.FastIoRead = NtfsCopyReadA; // Read
NtfsFastIoDispatch.FastIoWrite = NtfsCopyWriteA; // Write
NtfsFastIoDispatch.FastIoQueryBasicInfo = NtfsFastQueryBasicInfo; // QueryBasicInfo
NtfsFastIoDispatch.FastIoQueryStandardInfo = NtfsFastQueryStdInfo; // QueryStandardInfo
NtfsFastIoDispatch.FastIoLock = NtfsFastLock; // Lock
NtfsFastIoDispatch.FastIoUnlockSingle = NtfsFastUnlockSingle; // UnlockSingle
NtfsFastIoDispatch.FastIoUnlockAll = NtfsFastUnlockAll; // UnlockAll
NtfsFastIoDispatch.FastIoUnlockAllByKey = NtfsFastUnlockAllByKey; // UnlockAllByKey
NtfsFastIoDispatch.FastIoDeviceControl = NULL; // IoDeviceControl
NtfsFastIoDispatch.FastIoDetachDevice = NULL;
NtfsFastIoDispatch.FastIoQueryNetworkOpenInfo = NtfsFastQueryNetworkOpenInfo;
NtfsFastIoDispatch.AcquireFileForNtCreateSection = NtfsAcquireForCreateSection;
NtfsFastIoDispatch.ReleaseFileForNtCreateSection = NtfsReleaseForCreateSection;
NtfsFastIoDispatch.AcquireForModWrite = NtfsAcquireFileForModWrite;
NtfsFastIoDispatch.MdlRead = NtfsMdlReadA;
NtfsFastIoDispatch.MdlReadComplete = FsRtlMdlReadCompleteDev;
NtfsFastIoDispatch.PrepareMdlWrite = NtfsPrepareMdlWriteA;
NtfsFastIoDispatch.MdlWriteComplete = FsRtlMdlWriteCompleteDev;
#ifdef COMPRESS_ON_WIRE
NtfsFastIoDispatch.FastIoReadCompressed = NtfsCopyReadC;
NtfsFastIoDispatch.FastIoWriteCompressed = NtfsCopyWriteC;
NtfsFastIoDispatch.MdlReadCompleteCompressed = NtfsMdlReadCompleteCompressed;
NtfsFastIoDispatch.MdlWriteCompleteCompressed = NtfsMdlWriteCompleteCompressed;
#endif
NtfsFastIoDispatch.FastIoQueryOpen = NtfsNetworkOpenCreate;
NtfsFastIoDispatch.AcquireForCcFlush = NtfsAcquireFileForCcFlush;
NtfsFastIoDispatch.ReleaseForCcFlush = NtfsReleaseFileForCcFlush;
//
// Read the registry to determine if we should upgrade the volumes.
//
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
KeyName.Buffer = UPGRADE_CHECK_SETUP_KEY_NAME;
KeyName.Length = sizeof( UPGRADE_CHECK_SETUP_KEY_NAME ) - sizeof( WCHAR );
KeyName.MaximumLength = sizeof( UPGRADE_CHECK_SETUP_KEY_NAME );
ValueName.Buffer = UPGRADE_CHECK_SETUP_VALUE_NAME;
ValueName.Length = sizeof( UPGRADE_CHECK_SETUP_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( UPGRADE_CHECK_SETUP_VALUE_NAME );
//
// Look for the SystemSetupInProgress flag.
//
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
if (NT_SUCCESS( Status )) {
if (*((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset )) == 1) {
SetFlag( NtfsDataFlags, NTFS_FLAGS_DISABLE_UPGRADE );
}
//
// Otherwise look to see if the setupdd value is present.
//
} else {
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
KeyName.Buffer = UPGRADE_SETUPDD_KEY_NAME;
KeyName.Length = sizeof( UPGRADE_SETUPDD_KEY_NAME ) - sizeof( WCHAR );
KeyName.MaximumLength = sizeof( UPGRADE_SETUPDD_KEY_NAME );
ValueName.Buffer = UPGRADE_SETUPDD_VALUE_NAME;
ValueName.Length = sizeof( UPGRADE_SETUPDD_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( UPGRADE_SETUPDD_VALUE_NAME );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
//
// The presence of this flag says "Don't upgrade"
//
if (NT_SUCCESS( Status )) {
SetFlag( NtfsDataFlags, NTFS_FLAGS_DISABLE_UPGRADE );
}
}
//
// Read the registry to determine if we are to create short names.
//
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
KeyName.Buffer = COMPATIBILITY_MODE_KEY_NAME;
KeyName.Length = sizeof( COMPATIBILITY_MODE_KEY_NAME ) - sizeof( WCHAR );
KeyName.MaximumLength = sizeof( COMPATIBILITY_MODE_KEY_NAME );
ValueName.Buffer = COMPATIBILITY_MODE_VALUE_NAME;
ValueName.Length = sizeof( COMPATIBILITY_MODE_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( COMPATIBILITY_MODE_VALUE_NAME );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
//
// If we didn't find the value or the value is zero then create the 8.3
// names.
//
if (!NT_SUCCESS( Status ) ||
(*((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset )) == 0)) {
SetFlag( NtfsDataFlags, NTFS_FLAGS_CREATE_8DOT3_NAMES );
}
//
// Read the registry to determine if we allow extended character in short name.
//
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
ValueName.Buffer = EXTENDED_CHAR_MODE_VALUE_NAME;
ValueName.Length = sizeof( EXTENDED_CHAR_MODE_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( EXTENDED_CHAR_MODE_VALUE_NAME );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
//
// If we didn't find the value or the value is zero then do not allow
// extended character in 8.3 names.
//
if (NT_SUCCESS( Status ) &&
(*((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset )) == 1)) {
SetFlag( NtfsDataFlags, NTFS_FLAGS_ALLOW_EXTENDED_CHAR );
}
//
// Read the registry to determine if we should disable last access updates.
//
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
ValueName.Buffer = DISABLE_LAST_ACCESS_VALUE_NAME;
ValueName.Length = sizeof( DISABLE_LAST_ACCESS_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( DISABLE_LAST_ACCESS_VALUE_NAME );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
//
// If we didn't find the value or the value is zero then don't update last access times.
//
if (NT_SUCCESS( Status ) &&
(*((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset )) == 1)) {
SetFlag( NtfsDataFlags, NTFS_FLAGS_DISABLE_LAST_ACCESS );
}
//
// Read the registry to determine if we should change the Mft
// Zone reservation.
//
NtfsMftZoneMultiplier = 1;
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
ValueName.Buffer = MFT_ZONE_SIZE_VALUE_NAME;
ValueName.Length = sizeof( MFT_ZONE_SIZE_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( MFT_ZONE_SIZE_VALUE_NAME );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
//
// If we didn't find the value or the value is zero or greater than 4 then
// use the default.
//
if (NT_SUCCESS( Status )) {
ULONG NewMultiplier = *((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset ));
if ((NewMultiplier != 0) && (NewMultiplier <= 4)) {
NtfsMftZoneMultiplier = NewMultiplier;
}
}
//
// Read the registry to determine if we should change the Mft
// Zone reservation.
//
MemoryMultiplier = 1;
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
ValueName.Buffer = MEMORY_USAGE_VALUE_NAME;
ValueName.Length = sizeof( MEMORY_USAGE_VALUE_NAME ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( MEMORY_USAGE_VALUE_NAME );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
//
// If we didn't find the value or the value is zero or greater than 2 then
// use the default.
//
if (NT_SUCCESS( Status )) {
ULONG NewMultiplier = *((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset ));
if ((NewMultiplier != 0) && (NewMultiplier <= 2)) {
MemoryMultiplier = NewMultiplier;
}
}
//
// Read the registry to determine if the quota notification rate has been
// change from the default.
//
if (KeyValueInformation == NULL) {
DeallocateKeyValue = FALSE;
KeyValueLength = KEY_WORK_AREA;
KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer;
}
ValueName.Buffer = QUOTA_NOTIFY_RATE;
ValueName.Length = sizeof( QUOTA_NOTIFY_RATE ) - sizeof( WCHAR );
ValueName.MaximumLength = sizeof( QUOTA_NOTIFY_RATE );
Status = NtfsQueryValueKey( &KeyName, &ValueName, &KeyValueLength, &KeyValueInformation, &DeallocateKeyValue );
if (NT_SUCCESS( Status )) {
Value = *((PULONG) Add2Ptr( KeyValueInformation, KeyValueInformation->DataOffset ));
//
// Value is in second, convert it to 100ns.
//
NtfsMaxQuotaNotifyRate = (ULONGLONG) Value * 1000 * 1000 * 10;
}
//
// Initialize the global ntfs data structure
//
NtfsInitializeNtfsData( DriverObject, (USHORT) MemoryMultiplier );
//
// Remember the flags we collected from the registry above.
//
SetFlag( NtfsData.Flags, NtfsDataFlags );
if (NtfsRunningOnWhat( VER_SUITE_PERSONAL, VER_NT_WORKSTATION )) {
SetFlag( NtfsData.Flags, NTFS_FLAGS_PERSONAL );
}
KeInitializeMutant( &StreamFileCreationMutex, FALSE );
KeInitializeEvent( &NtfsEncryptionPendingEvent, NotificationEvent, TRUE );
//
// Initialize the Ntfs Mcb global data queue and variables
//
ExInitializeFastMutex( &NtfsMcbFastMutex );
InitializeListHead( &NtfsMcbLruQueue );
NtfsMcbCleanupInProgress = FALSE;
switch (MmQuerySystemSize()) {
case MmSmallSystem:
NtfsMcbHighWaterMark = 1000;
NtfsMcbLowWaterMark = 500;
NtfsMcbCurrentLevel = 0;
break;
case MmMediumSystem:
NtfsMcbHighWaterMark = 4000;
NtfsMcbLowWaterMark = 2000;
NtfsMcbCurrentLevel = 0;
break;
case MmLargeSystem:
default:
NtfsMcbHighWaterMark = 16000;
NtfsMcbLowWaterMark = 8000;
NtfsMcbCurrentLevel = 0;
break;
}
//
// Bias by the memory multiplier value from the registry.
//
NtfsMcbHighWaterMark *= MemoryMultiplier;
NtfsMcbLowWaterMark *= MemoryMultiplier;
NtfsMcbCurrentLevel *= MemoryMultiplier;
//
// Allocate and initialize the free Eresource array
//
if ((NtfsData.FreeEresourceArray =
NtfsAllocatePoolWithTagNoRaise( NonPagedPool, (NtfsData.FreeEresourceTotal * sizeof( PERESOURCE )), 'rftN')) == NULL) {
KeBugCheck( NTFS_FILE_SYSTEM );
}
RtlZeroMemory( NtfsData.FreeEresourceArray, NtfsData.FreeEresourceTotal * sizeof( PERESOURCE ));
//
// Keep a zeroed out object id extended info around for comparisons in objidsup.c.
//
RtlZeroMemory( NtfsZeroExtendedInfo, sizeof( NtfsZeroExtendedInfo ));
//
// Register the file system with the I/O system
//
IoRegisterFileSystem( DeviceObject );
//
// Initialize logging.
//
NtfsInitializeLogging();
//
// Initialize global variables. (ntfsdata.c assumes 2-digit value for
// $FILE_NAME)
//
ASSERT(($FILE_NAME >= 0x10) && ($FILE_NAME < 0x100));
ASSERT( ((BOOLEAN) IRP_CONTEXT_STATE_WAIT) != FALSE );
//
// Some big assumptions are made when these bits are set in create. Let's
// make sure those assumptions are still valid.
//
ASSERT( (READ_DATA_ACCESS == FILE_READ_DATA) &&
(WRITE_DATA_ACCESS == FILE_WRITE_DATA) &&
(APPEND_DATA_ACCESS == FILE_APPEND_DATA) &&
(WRITE_ATTRIBUTES_ACCESS == FILE_WRITE_ATTRIBUTES) &&
(EXECUTE_ACCESS == FILE_EXECUTE) &&
(BACKUP_ACCESS == (TOKEN_HAS_BACKUP_PRIVILEGE << 2)) &&
(RESTORE_ACCESS == (TOKEN_HAS_RESTORE_PRIVILEGE << 2)) );
//
// Let's make sure the number of attributes in the table is correct.
//
#ifdef NTFSDBG
{
ULONG Count = 0;
while (NtfsAttributeDefinitions[Count].AttributeTypeCode != $UNUSED) {
Count += 1;
}
//
// We want to add one for the empty end record.
//
Count += 1;
ASSERTMSG( "Update NtfsAttributeDefinitionsCount in attrdata.c",
(Count == NtfsAttributeDefinitionsCount) );
}
#endif
//
// Setup the CheckPointAllVolumes callback item, timer, dpc, and
// status.
//
ExInitializeWorkItem( &NtfsData.VolumeCheckpointItem,
NtfsCheckpointAllVolumes,
(PVOID)NULL );
KeInitializeTimer( &NtfsData.VolumeCheckpointTimer );
NtfsData.VolumeCheckpointStatus = 0;
KeInitializeDpc( &NtfsData.VolumeCheckpointDpc,
NtfsVolumeCheckpointDpc,
NULL );
NtfsData.TimerStatus = TIMER_NOT_SET;
//
// Setup the UsnTimeout callback item, timer, dpc, and
// status.
//
ExInitializeWorkItem( &NtfsData.UsnTimeOutItem,
NtfsCheckUsnTimeOut,
(PVOID)NULL );
KeInitializeTimer( &NtfsData.UsnTimeOutTimer );
KeInitializeDpc( &NtfsData.UsnTimeOutDpc,
NtfsUsnTimeOutDpc,
NULL );
{
LONGLONG FiveMinutesFromNow = -5*1000*1000*10;
FiveMinutesFromNow *= 60;
KeSetTimer( &NtfsData.UsnTimeOutTimer,
*(PLARGE_INTEGER)&FiveMinutesFromNow,
&NtfsData.UsnTimeOutDpc );
}
//
// Initialize sync objects for reserved buffers
//
ExInitializeFastMutex( &NtfsReservedBufferMutex );
ExInitializeResource( &NtfsReservedBufferResource );
//
// Zero out the global upcase table, that way we'll fill it in on
// our first successful mount
//
NtfsData.UpcaseTable = NULL;
NtfsData.UpcaseTableSize = 0;
ExInitializeFastMutex( &NtfsScavengerLock );
NtfsScavengerWorkList = NULL;
NtfsScavengerRunning = FALSE;
//
// Initialize the EFS driver
//
IoRegisterDriverReinitialization( DriverObject, NtfsLoadAddOns, NULL );
//
// And return to our caller
//
return( STATUS_SUCCESS );
}
VOID
NtfsInitializeNtfsData (
IN PDRIVER_OBJECT DriverObject,
IN USHORT MemoryMultiplier
)
/*++
Routine Description:
This routine initializes the global ntfs data record
Arguments:
DriverObject - Supplies the driver object for NTFS.
MemoryMultiplier - Amount to multiply the memory usage.
Return Value:
None.
--*/
{
USHORT FileLockMaxDepth;
USHORT IoContextMaxDepth;
USHORT IrpContextMaxDepth;
USHORT KeventMaxDepth;
USHORT ScbNonpagedMaxDepth;
USHORT ScbSnapshotMaxDepth;
USHORT CcbDataMaxDepth;
USHORT CcbMaxDepth;
USHORT DeallocatedRecordsMaxDepth;
USHORT FcbDataMaxDepth;
USHORT FcbIndexMaxDepth;
USHORT IndexContextMaxDepth;
USHORT LcbMaxDepth;
USHORT NukemMaxDepth;
USHORT ScbDataMaxDepth;
USHORT CompSyncMaxDepth;
PSECURITY_SUBJECT_CONTEXT SubjectContext = NULL;
BOOLEAN CapturedSubjectContext = FALSE;
PACL SystemDacl = NULL;
ULONG SystemDaclLength;
PSID AdminSid = NULL;
PSID SystemSid = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
//
// Zero the record.
//
RtlZeroMemory( &NtfsData, sizeof(NTFS_DATA));
//
// Initialize the queue of mounted Vcbs
//
InitializeListHead(&NtfsData.VcbQueue);
//
// This list head keeps track of closes yet to be done.
//
InitializeListHead( &NtfsData.AsyncCloseList );
InitializeListHead( &NtfsData.DelayedCloseList );
ExInitializeWorkItem( &NtfsData.NtfsCloseItem,
(PWORKER_THREAD_ROUTINE)NtfsFspClose,
NULL );
//
// Set the driver object, device object, and initialize the global
// resource protecting the file system
//
NtfsData.DriverObject = DriverObject;
ExInitializeResource( &NtfsData.Resource );
ExInitializeFastMutex( &NtfsData.NtfsDataLock );
//
// Now allocate and initialize the s-list structures used as our pool
// of IRP context records. The size of the zone is based on the
// system memory size. We also initialize the spin lock used to protect
// the zone.
//
{
switch ( MmQuerySystemSize() ) {
case MmSmallSystem:
NtfsData.FreeEresourceTotal = 14;
//
// Nonpaged Lookaside list maximum depths
//
FileLockMaxDepth = 8;
IoContextMaxDepth = 8;
IrpContextMaxDepth = 4;
KeventMaxDepth = 8;
ScbNonpagedMaxDepth = 8;
ScbSnapshotMaxDepth = 8;
CompSyncMaxDepth = 4;
//
// Paged Lookaside list maximum depths
//
CcbDataMaxDepth = 4;
CcbMaxDepth = 4;
DeallocatedRecordsMaxDepth = 8;
FcbDataMaxDepth = 8;
FcbIndexMaxDepth = 4;
IndexContextMaxDepth = 8;
LcbMaxDepth = 4;
NukemMaxDepth = 8;
ScbDataMaxDepth = 4;
SetFlag( NtfsData.Flags, NTFS_FLAGS_SMALL_SYSTEM );
NtfsMaxDelayedCloseCount = MAX_DELAYED_CLOSE_COUNT;
NtfsAsyncPostThreshold = ASYNC_CLOSE_POST_THRESHOLD;
break;
case MmMediumSystem:
NtfsData.FreeEresourceTotal = 30;
//
// Nonpaged Lookaside list maximum depths
//
FileLockMaxDepth = 8;
IoContextMaxDepth = 8;
IrpContextMaxDepth = 8;
KeventMaxDepth = 8;
ScbNonpagedMaxDepth = 30;
ScbSnapshotMaxDepth = 8;
CompSyncMaxDepth = 8;
//
// Paged Lookaside list maximum depths
//
CcbDataMaxDepth = 12;
CcbMaxDepth = 6;
DeallocatedRecordsMaxDepth = 8;
FcbDataMaxDepth = 30;
FcbIndexMaxDepth = 12;
IndexContextMaxDepth = 8;
LcbMaxDepth = 12;
NukemMaxDepth = 8;
ScbDataMaxDepth = 12;
SetFlag( NtfsData.Flags, NTFS_FLAGS_MEDIUM_SYSTEM );
NtfsMaxDelayedCloseCount = 4 * MAX_DELAYED_CLOSE_COUNT;
NtfsAsyncPostThreshold = 4 * ASYNC_CLOSE_POST_THRESHOLD;
break;
case MmLargeSystem:
SetFlag( NtfsData.Flags, NTFS_FLAGS_LARGE_SYSTEM );
NtfsMaxDelayedCloseCount = 16 * MAX_DELAYED_CLOSE_COUNT;
NtfsAsyncPostThreshold = 16 * ASYNC_CLOSE_POST_THRESHOLD;
if (MmIsThisAnNtAsSystem()) {
NtfsData.FreeEresourceTotal = 256;
//
// Nonpaged Lookaside list maximum depths
//
FileLockMaxDepth = 8;
IoContextMaxDepth = 8;
IrpContextMaxDepth = 256;
KeventMaxDepth = 8;
ScbNonpagedMaxDepth = 128;
ScbSnapshotMaxDepth = 8;
CompSyncMaxDepth = 32;
//
// Paged Lookaside list maximum depths
//
CcbDataMaxDepth = 40;
CcbMaxDepth = 20;
DeallocatedRecordsMaxDepth = 8;
FcbDataMaxDepth = 128;
FcbIndexMaxDepth = 40;
IndexContextMaxDepth = 8;
LcbMaxDepth = 40;
NukemMaxDepth = 8;
ScbDataMaxDepth = 40;
} else {
NtfsData.FreeEresourceTotal = 128;
//
// Nonpaged Lookaside list maximum depths
//
FileLockMaxDepth = 8;
IoContextMaxDepth = 8;
IrpContextMaxDepth = 64;
KeventMaxDepth = 8;
ScbNonpagedMaxDepth = 64;
ScbSnapshotMaxDepth = 8;
CompSyncMaxDepth = 16;
//
// Paged Lookaside list maximum depths
//
CcbDataMaxDepth = 20;
CcbMaxDepth = 10;
DeallocatedRecordsMaxDepth = 8;
FcbDataMaxDepth = 64;
FcbIndexMaxDepth = 20;
IndexContextMaxDepth = 8;
LcbMaxDepth = 20;
NukemMaxDepth = 8;
ScbDataMaxDepth = 20;
}
break;
}
NtfsMinDelayedCloseCount = NtfsMaxDelayedCloseCount * 4 / 5;
NtfsThrottleCreates = NtfsMinDelayedCloseCount * 2;
//
// Now bias by the memory multiplier value.
//
NtfsMaxDelayedCloseCount *= MemoryMultiplier;
NtfsAsyncPostThreshold *= MemoryMultiplier;
NtfsData.FreeEresourceTotal *= MemoryMultiplier;
FileLockMaxDepth *= MemoryMultiplier;
IoContextMaxDepth *= MemoryMultiplier;
IrpContextMaxDepth *= MemoryMultiplier;
KeventMaxDepth *= MemoryMultiplier;
ScbNonpagedMaxDepth *= MemoryMultiplier;
ScbSnapshotMaxDepth *= MemoryMultiplier;
CompSyncMaxDepth *= MemoryMultiplier;
CcbDataMaxDepth *= MemoryMultiplier;
CcbMaxDepth *= MemoryMultiplier;
DeallocatedRecordsMaxDepth *= MemoryMultiplier;
FcbDataMaxDepth *= MemoryMultiplier;
FcbIndexMaxDepth *= MemoryMultiplier;
IndexContextMaxDepth *= MemoryMultiplier;
LcbMaxDepth *= MemoryMultiplier;
NukemMaxDepth *= MemoryMultiplier;
ScbDataMaxDepth *= MemoryMultiplier;
NtfsMinDelayedCloseCount *= MemoryMultiplier;
NtfsThrottleCreates *= MemoryMultiplier;
}
//
// Initialize our various lookaside lists. To make it a bit more readable we'll
// define two quick macros to do the initialization
//
#if DBG && i386 && defined (NTFSPOOLCHECK)
#define NPagedInit(L,S,T,D) { ExInitializeNPagedLookasideList( (L), NtfsDebugAllocatePoolWithTag, NtfsDebugFreePool, POOL_RAISE_IF_ALLOCATION_FAILURE, S, T, D); }
#define PagedInit(L,S,T,D) { ExInitializePagedLookasideList( (L), NtfsDebugAllocatePoolWithTag, NtfsDebugFreePool, POOL_RAISE_IF_ALLOCATION_FAILURE, S, T, D); }
#else // DBG && i386
#define NPagedInit(L,S,T,D) { ExInitializeNPagedLookasideList( (L), NULL, NULL, POOL_RAISE_IF_ALLOCATION_FAILURE, S, T, D); }
#define PagedInit(L,S,T,D) { ExInitializePagedLookasideList( (L), NULL, NULL, POOL_RAISE_IF_ALLOCATION_FAILURE, S, T, D); }
#endif // DBG && i386
NPagedInit( &NtfsIoContextLookasideList, sizeof(NTFS_IO_CONTEXT), 'IftN', IoContextMaxDepth );
NPagedInit( &NtfsIrpContextLookasideList, sizeof(IRP_CONTEXT), 'iftN', IrpContextMaxDepth );
NPagedInit( &NtfsKeventLookasideList, sizeof(KEVENT), 'KftN', KeventMaxDepth );
NPagedInit( &NtfsScbNonpagedLookasideList, sizeof(SCB_NONPAGED), 'nftN', ScbNonpagedMaxDepth );
NPagedInit( &NtfsScbSnapshotLookasideList, sizeof(SCB_SNAPSHOT), 'TftN', ScbSnapshotMaxDepth );
//
// The compresson sync routine needs its own allocate and free routine in order to initialize and
// cleanup the embedded resource.
//
ExInitializeNPagedLookasideList( &NtfsCompressSyncLookasideList,
NtfsAllocateCompressionSync,
NtfsDeallocateCompressionSync,
0,
sizeof( COMPRESSION_SYNC ),
'vftN',
CompSyncMaxDepth );
PagedInit( &NtfsCcbLookasideList, sizeof(CCB), 'CftN', CcbMaxDepth );
PagedInit( &NtfsCcbDataLookasideList, sizeof(CCB_DATA), 'cftN', CcbDataMaxDepth );
PagedInit( &NtfsDeallocatedRecordsLookasideList, sizeof(DEALLOCATED_RECORDS), 'DftN', DeallocatedRecordsMaxDepth );
PagedInit( &NtfsFcbDataLookasideList, sizeof(FCB_DATA), 'fftN', FcbDataMaxDepth );
PagedInit( &NtfsFcbIndexLookasideList, sizeof(FCB_INDEX), 'FftN', FcbIndexMaxDepth );
PagedInit( &NtfsIndexContextLookasideList, sizeof(INDEX_CONTEXT), 'EftN', IndexContextMaxDepth );
PagedInit( &NtfsLcbLookasideList, sizeof(LCB), 'lftN', LcbMaxDepth );
PagedInit( &NtfsNukemLookasideList, sizeof(NUKEM), 'NftN', NukemMaxDepth );
PagedInit( &NtfsScbDataLookasideList, SIZEOF_SCB_DATA, 'sftN', ScbDataMaxDepth );
//
// Initialize the cache manager callback routines, First are the routines
// for normal file manipulations, followed by the routines for
// volume manipulations.
//
{
PCACHE_MANAGER_CALLBACKS Callbacks = &NtfsData.CacheManagerCallbacks;
Callbacks->AcquireForLazyWrite = &NtfsAcquireScbForLazyWrite;
Callbacks->ReleaseFromLazyWrite = &NtfsReleaseScbFromLazyWrite;
Callbacks->AcquireForReadAhead = &NtfsAcquireScbForReadAhead;
Callbacks->ReleaseFromReadAhead = &NtfsReleaseScbFromReadAhead;
}
{
PCACHE_MANAGER_CALLBACKS Callbacks = &NtfsData.CacheManagerVolumeCallbacks;
Callbacks->AcquireForLazyWrite = &NtfsAcquireVolumeFileForLazyWrite;
Callbacks->ReleaseFromLazyWrite = &NtfsReleaseVolumeFileFromLazyWrite;
Callbacks->AcquireForReadAhead = NULL;
Callbacks->ReleaseFromReadAhead = NULL;
}
//
// Initialize the queue of read ahead threads
//
InitializeListHead(&NtfsData.ReadAheadThreads);
//
// Set up global pointer to our process.
//
NtfsData.OurProcess = PsGetCurrentProcess();
//
// Use a try-finally to cleanup on errors.
//
try {
SECURITY_DESCRIPTOR NewDescriptor;
SID_IDENTIFIER_AUTHORITY Authority = SECURITY_NT_AUTHORITY;
SubjectContext = NtfsAllocatePool( PagedPool, sizeof( SECURITY_SUBJECT_CONTEXT ));
SeCaptureSubjectContext( SubjectContext );
CapturedSubjectContext = TRUE;
//
// Build the default security descriptor which gives full access to
// system and administrator.
//
AdminSid = (PSID) NtfsAllocatePool( PagedPool, RtlLengthRequiredSid( 2 ));
RtlInitializeSid( AdminSid, &Authority, 2 );
*(RtlSubAuthoritySid( AdminSid, 0 )) = SECURITY_BUILTIN_DOMAIN_RID;
*(RtlSubAuthoritySid( AdminSid, 1 )) = DOMAIN_ALIAS_RID_ADMINS;
SystemSid = (PSID) NtfsAllocatePool( PagedPool, RtlLengthRequiredSid( 1 ));
RtlInitializeSid( SystemSid, &Authority, 1 );
*(RtlSubAuthoritySid( SystemSid, 0 )) = SECURITY_LOCAL_SYSTEM_RID;
SystemDaclLength = sizeof( ACL ) +
(2 * sizeof( ACCESS_ALLOWED_ACE )) +
SeLengthSid( AdminSid ) +
SeLengthSid( SystemSid ) +
8; // The 8 is just for good measure
SystemDacl = NtfsAllocatePool( PagedPool, SystemDaclLength );
Status = RtlCreateAcl( SystemDacl, SystemDaclLength, ACL_REVISION2 );
if (!NT_SUCCESS( Status )) { leave; }
Status = RtlAddAccessAllowedAce( SystemDacl,
ACL_REVISION2,
GENERIC_ALL,
SystemSid );
if (!NT_SUCCESS( Status )) { leave; }
Status = RtlAddAccessAllowedAce( SystemDacl,
ACL_REVISION2,
GENERIC_ALL,
AdminSid );
if (!NT_SUCCESS( Status )) { leave; }
Status = RtlCreateSecurityDescriptor( &NewDescriptor,
SECURITY_DESCRIPTOR_REVISION1 );
if (!NT_SUCCESS( Status )) { leave; }
Status = RtlSetDaclSecurityDescriptor( &NewDescriptor,
TRUE,
SystemDacl,
FALSE );
if (!NT_SUCCESS( Status )) { leave; }
Status = SeAssignSecurity( NULL,
&NewDescriptor,
&NtfsData.DefaultDescriptor,
FALSE,
SubjectContext,
IoGetFileObjectGenericMapping(),
PagedPool );
if (!NT_SUCCESS( Status )) { leave; }
NtfsData.DefaultDescriptorLength = RtlLengthSecurityDescriptor( NtfsData.DefaultDescriptor );
ASSERT( SeValidSecurityDescriptor( NtfsData.DefaultDescriptorLength,
NtfsData.DefaultDescriptor ));
} finally {
if (CapturedSubjectContext) {
SeReleaseSubjectContext( SubjectContext );
}
if (SubjectContext != NULL) { NtfsFreePool( SubjectContext ); }
if (SystemDacl != NULL) { NtfsFreePool( SystemDacl ); }
if (AdminSid != NULL) { NtfsFreePool( AdminSid ); }
if (SystemSid != NULL) { NtfsFreePool( SystemSid ); }
}
//
// Raise if we hit an error building the security descriptor.
//
if (!NT_SUCCESS( Status )) { ExRaiseStatus( Status ); }
//
// Set its node type code and size. We do this last as a flag to indicate that the structure is
// initialized.
//
NtfsData.NodeTypeCode = NTFS_NTC_DATA_HEADER;
NtfsData.NodeByteSize = sizeof(NTFS_DATA);
#ifdef SYSCACHE_DEBUG
{
int Index;
for (Index=0; Index < NUM_SC_LOGSETS; Index++) {
NtfsSyscacheLogSet[Index].SyscacheLog = 0;
NtfsSyscacheLogSet[Index].Scb = 0;
}
NtfsCurrentSyscacheLogSet = -1;
NtfsCurrentSyscacheOnDiskEntry = -1;
}
#endif
//
// And return to our caller
//
return;
}
//
// Local Support routine
//
NTSTATUS
NtfsQueryValueKey (
IN PUNICODE_STRING KeyName,
IN PUNICODE_STRING ValueName,
IN OUT PULONG ValueLength,
IN OUT PKEY_VALUE_FULL_INFORMATION *KeyValueInformation,
IN OUT PBOOLEAN DeallocateKeyValue
)
/*++
Routine Description:
Given a unicode value name this routine will return the registry
information for the given key and value.
Arguments:
KeyName - the unicode name for the key being queried.
ValueName - the unicode name for the registry value located in the registry.
ValueLength - On input it is the length of the allocated buffer. On output
it is the length of the buffer. It may change if the buffer is
reallocated.
KeyValueInformation - On input it points to the buffer to use to query the
the value information. On output it points to the buffer used to
perform the query. It may change if a larger buffer is needed.
DeallocateKeyValue - Indicates if the KeyValueInformation buffer is on the
stack or needs to be deallocated.
Return Value:
NTSTATUS - indicates the status of querying the registry.
--*/
{
HANDLE Handle;
NTSTATUS Status;
ULONG RequestLength;
ULONG ResultLength;
OBJECT_ATTRIBUTES ObjectAttributes;
PVOID NewKey;
InitializeObjectAttributes( &ObjectAttributes,
KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwOpenKey( &Handle,
KEY_READ,
&ObjectAttributes);
if (!NT_SUCCESS( Status )) {
return Status;
}
RequestLength = *ValueLength;
while (TRUE) {
Status = ZwQueryValueKey( Handle,
ValueName,
KeyValueFullInformation,
*KeyValueInformation,
RequestLength,
&ResultLength);
ASSERT( Status != STATUS_BUFFER_OVERFLOW );
if (Status == STATUS_BUFFER_OVERFLOW) {
//
// Try to get a buffer big enough.
//
if (*DeallocateKeyValue) {
NtfsFreePool( *KeyValueInformation );
*ValueLength = 0;
*KeyValueInformation = NULL;
*DeallocateKeyValue = FALSE;
}
RequestLength += 256;
NewKey = (PKEY_VALUE_FULL_INFORMATION)
NtfsAllocatePoolWithTagNoRaise( PagedPool,
RequestLength,
'xftN');
if (NewKey == NULL) {
return STATUS_NO_MEMORY;
}
*KeyValueInformation = NewKey;
*ValueLength = RequestLength;
*DeallocateKeyValue = TRUE;
} else {
break;
}
}
ZwClose(Handle);
if (NT_SUCCESS(Status)) {
//
// Treat as if no value was found if the data length is zero.
//
if ((*KeyValueInformation)->DataLength == 0) {
Status = STATUS_OBJECT_NAME_NOT_FOUND;
}
}
return Status;
}
BOOLEAN
NtfsRunningOnWhat(
IN USHORT SuiteMask,
IN UCHAR ProductType
)
/*++
Routine Description:
This function checks the system to see if
NTFS is running on a specified version of
the operating system.
The different versions are denoted by the product
id and the product suite.
Arguments:
SuiteMask - The mask that specifies the requested suite(s)
ProductType - The product type that specifies the requested product type
Return Value:
TRUE if NTFS is running on the requested version
FALSE otherwise.
--*/
{
OSVERSIONINFOEXW OsVer = {0};
ULONGLONG ConditionMask = 0;
PAGED_CODE();
OsVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
OsVer.wSuiteMask = SuiteMask;
OsVer.wProductType = ProductType;
VER_SET_CONDITION( ConditionMask, VER_PRODUCT_TYPE, VER_EQUAL );
VER_SET_CONDITION( ConditionMask, VER_SUITENAME, VER_AND );
return RtlVerifyVersionInfo( &OsVer,
VER_PRODUCT_TYPE | VER_SUITENAME,
ConditionMask) == STATUS_SUCCESS;
}