1342 lines
42 KiB
C
1342 lines
42 KiB
C
/*++
|
||
|
||
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
|
||
);
|
||
|
||
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 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;
|
||
|
||
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;
|
||
|
||
//
|
||
// Initialize the global ntfs data structure
|
||
//
|
||
|
||
NtfsInitializeNtfsData( DriverObject );
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// Double the watermark levels for all
|
||
// systems running data center or server appliance
|
||
//
|
||
|
||
if (NtfsRunningOnWhat( VER_SUITE_DATACENTER, VER_NT_SERVER ) ||
|
||
NtfsRunningOnWhat( VER_SUITE_DATACENTER, VER_NT_DOMAIN_CONTROLLER )) {
|
||
|
||
NtfsMcbHighWaterMark <<= 1;
|
||
NtfsMcbLowWaterMark <<= 1;
|
||
}
|
||
|
||
//
|
||
// 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
|
||
|
||
//
|
||
// 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( NtfsData.Flags, 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( NtfsData.Flags, 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( NtfsData.Flags, 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( NtfsData.Flags, 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( NtfsData.Flags, 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 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;
|
||
}
|
||
|
||
//
|
||
// 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
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the global ntfs data record
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Supplies the driver object for NTFS
|
||
|
||
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;
|
||
}
|
||
|
||
//
|
||
// 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;
|
||
}
|