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
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;
|
|
}
|