/*++ 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; }