BOOLEAN CmpFileWrite( PHHIVE Hive, ULONG FileType, PCMP_OFFSET_ARRAY offsetArray, ULONG offsetArrayCount, PULONG FileOffset ) /*++ Routine Description: This routine writes an array of buffers out to a file. It is environment specific. NOTE: We assume the handle is opened for asynchronous access, and that we, and not the IO system, are keeping the offset pointer. NOTE: Only 32bit offsets are supported, even though the underlying IO system on NT supports 64 bit offsets. Arguments: Hive - Hive we are doing I/O for FileType - which supporting file to use offsetArray - array of structures where each structure holds a 32bit offset into the Hive file and pointer the a buffer written to that file offset. offsetArrayCount - number of elements in the offsetArray. FileOffset - returns the file offset after the last write to the file. Return Value: FALSE if failure TRUE if success --*/ { NTSTATUS status; LARGE_INTEGER Offset; PCMHIVE CmHive; HANDLE FileHandle; ULONG LengthToWrite; LONG WaitBufferCount = 0; LONG idx; ULONG arrayCount = 0; PVOID DataBuffer = NULL; // W4 only ULONG DataLength; BOOLEAN ret_val = TRUE; PCM_WRITE_BLOCK WriteBlock = NULL; if (CmpNoWrite) { return TRUE; } ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0); CmHive = (PCMHIVE)Hive; FileHandle = CmHive->FileHandles[FileType]; if (FileHandle == NULL) { return TRUE; } CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileWrite:\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"\tHandle=%08lx ", FileHandle)); // // decide whether we wait for IOs to complete or just issue them and // rely on the CcFlushCache to do the job // // Bring pages being written into memory first to allow disk to write // buffer contiguously. try { for (idx = 0; (ULONG) idx < offsetArrayCount; idx++) { char * start = offsetArray[idx].DataBuffer; char * end = (char *) start + offsetArray[idx].DataLength; while (start < end) { // perftouchbuffer globally declared so that compiler won't try // to remove it and this loop (if its smart enough?). perftouchbuffer += (ULONG) *start; start += PAGE_SIZE; } } } except (EXCEPTION_EXECUTE_HANDLER) { // // we might get STATUS_IN_PAGE_ERROR // CmKdPrintEx((DPFLTR_CONFIG_ID,DPFLTR_ERROR_LEVEL,"CmpFileWrite has raised :%08lx\n",GetExceptionCode())); return FALSE; } WriteBlock = (PCM_WRITE_BLOCK)ExAllocatePool(NonPagedPool,sizeof(CM_WRITE_BLOCK)); if( WriteBlock == NULL ) { return FALSE; } for (idx = 0; idx < MAXIMUM_WAIT_OBJECTS; idx++) { WriteBlock->EventHandles[idx] = NULL; #if DBG WriteBlock->EventObjects[idx] = NULL; #endif } // // We'd really like to just call the filesystems and have them do // the right thing. But the filesystem will attempt to lock our // entire buffer into memory, and that may fail for large requests. // So we split our reads into 64k chunks and call the filesystem for // each one. // ASSERT_PASSIVE_LEVEL(); arrayCount = 0; DataLength = 0; // This outer loop is hit more than once if the MAXIMUM_WAIT_OBJECTS limit // is hit before the offset array is drained. while (arrayCount < offsetArrayCount) { WaitBufferCount = 0; // This loop fills the wait buffer. while ((arrayCount < offsetArrayCount) && (WaitBufferCount < MAXIMUM_WAIT_OBJECTS)) { // If data length isn't zero than the wait buffer filled before the // buffer in the last offsetArray element was sent to write file. if (DataLength == 0) { *FileOffset = offsetArray[arrayCount].FileOffset; DataBuffer = offsetArray[arrayCount].DataBuffer; DataLength = offsetArray[arrayCount].DataLength; // // Detect attempt to read off end of 2gig file // (this should be irrelevant) // if ((0xffffffff - *FileOffset) < DataLength) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileWrite: runoff\n")); status = STATUS_INVALID_PARAMETER_5; goto Error_Exit; } } // else still more to write out of last buffer. while ((DataLength > 0) && (WaitBufferCount < MAXIMUM_WAIT_OBJECTS)) { // // Convert ULONG to Large // Offset.LowPart = *FileOffset; Offset.HighPart = 0L; // // trim request down if necessary. // if (DataLength > MAX_FILE_IO) { LengthToWrite = MAX_FILE_IO; } else { LengthToWrite = DataLength; } // Previously created events are reused. if (WriteBlock->EventHandles[WaitBufferCount] == NULL) { status = CmpCreateEvent(SynchronizationEvent, &(WriteBlock->EventHandles[WaitBufferCount]), &(WriteBlock->EventObjects[WaitBufferCount])); if (!NT_SUCCESS(status)) { // Make sure we don't try to clean this up. WriteBlock->EventHandles[WaitBufferCount] = NULL; goto Error_Exit; } CmpSetHandleProtection(WriteBlock->EventHandles[WaitBufferCount],TRUE); } status = ZwWriteFile(FileHandle, WriteBlock->EventHandles[WaitBufferCount], NULL, // apcroutine NULL, // apccontext &(WriteBlock->IoStatus[WaitBufferCount]), DataBuffer, LengthToWrite, &Offset, NULL); if (!NT_SUCCESS(status)) { goto Error_Exit; } WaitBufferCount++; // // adjust offsets // *FileOffset = Offset.LowPart + LengthToWrite; DataLength -= LengthToWrite; DataBuffer = (PVOID)((PCHAR)DataBuffer + LengthToWrite); } // while (DataLength > 0 && WaitBufferCount < MAXIMUM_WAIT_OBJECTS) arrayCount++; } // while (arrayCount < offsetArrayCount && // WaitBufferCount < MAXIMUM_WAIT_OBJECTS) status = KeWaitForMultipleObjects(WaitBufferCount, WriteBlock->EventObjects, WaitAll, Executive, KernelMode, FALSE, NULL, WriteBlock->WaitBlockArray); if (!NT_SUCCESS(status)) goto Error_Exit; for (idx = 0; idx < WaitBufferCount; idx++) { if (!NT_SUCCESS(WriteBlock->IoStatus[idx].Status)) { status = WriteBlock->IoStatus[idx].Status; ret_val = FALSE; goto Done; } } // There may still be more to do if the last element held a big buffer // and the wait buffer filled before it was all sent to the file. if (DataLength > 0) { arrayCount--; } } // while (arrayCount < offsetArrayCount) ret_val = TRUE; goto Done; Error_Exit: // // set debugging info // CmRegistryIODebug.Action = CmpIoFileWrite; CmRegistryIODebug.Handle = FileHandle; CmRegistryIODebug.Status = status; #if DBG DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileWrite: error exiting %d\n", status); #endif // // if WaitBufferCount > 0 then we have successfully issued // some I/Os, but not all of them. This is an error, but we // cannot return from this routine until all the successfully // issued I/Os have completed. // if (WaitBufferCount > 0) { // // only if we decided that we want to wait for the write to complete // (log files and hives not using the mapped views technique) // status = KeWaitForMultipleObjects(WaitBufferCount, WriteBlock->EventObjects, WaitAll, Executive, KernelMode, FALSE, NULL, WriteBlock->WaitBlockArray); } ret_val = FALSE; Done: idx = 0; // Clean up open event handles and objects. while ((idx < MAXIMUM_WAIT_OBJECTS) && (WriteBlock->EventHandles[idx] != NULL)) { ASSERT( WriteBlock->EventObjects[idx] ); ObDereferenceObject(WriteBlock->EventObjects[idx]); CmCloseHandle(WriteBlock->EventHandles[idx]); idx++; } if( WriteBlock != NULL ) { ExFreePool(WriteBlock); } return ret_val; }
NTSTATUS NTAPI CmpOpenHiveFiles(IN PCUNICODE_STRING BaseName, IN PCWSTR Extension OPTIONAL, OUT PHANDLE Primary, OUT PHANDLE Log, OUT PULONG PrimaryDisposition, OUT PULONG LogDisposition, IN BOOLEAN CreateAllowed, IN BOOLEAN MarkAsSystemHive, IN BOOLEAN NoBuffering, OUT PULONG ClusterSize OPTIONAL) { HANDLE EventHandle; PKEVENT Event; NTSTATUS Status; UNICODE_STRING FullName, ExtensionName; PWCHAR NameBuffer; USHORT Length; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; ULONG AttributeFlags, ShareMode, DesiredAccess, CreateDisposition, IoFlags; USHORT CompressionState; FILE_STANDARD_INFORMATION FileInformation; FILE_FS_SIZE_INFORMATION FsSizeInformation; /* Create event */ Status = CmpCreateEvent(NotificationEvent, &EventHandle, &Event); if (!NT_SUCCESS(Status)) return Status; /* Initialize the full name */ RtlInitEmptyUnicodeString(&FullName, NULL, 0); Length = BaseName->Length; /* Check if we have an extension */ if (Extension) { /* Update the name length */ Length += (USHORT)wcslen(Extension) * sizeof(WCHAR) + sizeof(UNICODE_NULL); /* Allocate the buffer for the full name */ NameBuffer = ExAllocatePoolWithTag(PagedPool, Length, TAG_CM); if (!NameBuffer) { /* Fail */ ObDereferenceObject(Event); ZwClose(EventHandle); return STATUS_NO_MEMORY; } /* Build the full name */ FullName.Buffer = NameBuffer; FullName.MaximumLength = Length; RtlAppendUnicodeStringToString(&FullName, BaseName); } else { /* The base name is the full name */ FullName = *BaseName; NameBuffer = NULL; } /* Initialize the attributes */ InitializeObjectAttributes(&ObjectAttributes, &FullName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); /* Check if we can create the hive */ if ((CreateAllowed) && !(CmpShareSystemHives)) { /* Open only or create */ CreateDisposition = FILE_OPEN_IF; } else { /* Open only */ CreateDisposition = FILE_OPEN; } /* Setup the flags */ // FIXME : FILE_OPEN_FOR_BACKUP_INTENT is unimplemented and breaks 3rd stage boot IoFlags = //FILE_OPEN_FOR_BACKUP_INTENT | FILE_NO_COMPRESSION | FILE_RANDOM_ACCESS | (NoBuffering ? FILE_NO_INTERMEDIATE_BUFFERING : 0); /* Set share and access modes */ if ((CmpMiniNTBoot) && (CmpShareSystemHives)) { /* We're on Live CD or otherwise sharing */ DesiredAccess = FILE_READ_DATA; ShareMode = FILE_SHARE_READ; } else { /* We want to write exclusively */ ShareMode = 0; DesiredAccess = FILE_READ_DATA | FILE_WRITE_DATA; } /* Default attributes */ AttributeFlags = FILE_ATTRIBUTE_NORMAL; /* Now create the file */ Status = ZwCreateFile(Primary, DesiredAccess | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, AttributeFlags, ShareMode, CreateDisposition, FILE_SYNCHRONOUS_IO_NONALERT | IoFlags, NULL, 0); /* Check if anything failed until now */ if (!NT_SUCCESS(Status)) { /* Close handles and free buffers */ if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM); ObDereferenceObject(Event); ZwClose(EventHandle); DPRINT1("ZwCreateFile failed : %lx.\n", Status); *Primary = NULL; return Status; } if (MarkAsSystemHive) { /* We opened it, mark it as a system hive */ Status = ZwFsControlFile(*Primary, EventHandle, NULL, NULL, &IoStatusBlock, FSCTL_MARK_AS_SYSTEM_HIVE, NULL, 0, NULL, 0); if (Status == STATUS_PENDING) { /* Wait for completion */ KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* If we don't support it, ignore the failure */ if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS; if (!NT_SUCCESS(Status)) { /* Close handles and free buffers */ if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM); ObDereferenceObject(Event); ZwClose(EventHandle); ZwClose(*Primary); *Primary = NULL; return Status; } } /* Disable compression */ CompressionState = 0; Status = ZwFsControlFile(*Primary, EventHandle, NULL, NULL, &IoStatusBlock, FSCTL_SET_COMPRESSION, &CompressionState, sizeof(CompressionState), NULL, 0); if (Status == STATUS_PENDING) { /* Wait for completion */ KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL); } /* Get the disposition */ *PrimaryDisposition = (ULONG)IoStatusBlock.Information; if (IoStatusBlock.Information != FILE_CREATED) { /* Check how large the file is */ Status = ZwQueryInformationFile(*Primary, &IoStatusBlock, &FileInformation, sizeof(FileInformation), FileStandardInformation); if (NT_SUCCESS(Status)) { /* Check if it's 0 bytes */ if (!FileInformation.EndOfFile.QuadPart) { /* Assume it's a new file */ *PrimaryDisposition = FILE_CREATED; } } } /* Check if the caller wants cluster size returned */ if (ClusterSize) { /* Query it */ Status = ZwQueryVolumeInformationFile(*Primary, &IoStatusBlock, &FsSizeInformation, sizeof(FsSizeInformation), FileFsSizeInformation); if (!NT_SUCCESS(Status)) { /* Close handles and free buffers */ if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM); ObDereferenceObject(Event); ZwClose(EventHandle); return Status; } /* Check if the sector size is invalid */ if (FsSizeInformation.BytesPerSector > HBLOCK_SIZE) { /* Close handles and free buffers */ if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM); ObDereferenceObject(Event); ZwClose(EventHandle); return STATUS_CANNOT_LOAD_REGISTRY_FILE; } /* Return cluster size */ *ClusterSize = max(1, FsSizeInformation.BytesPerSector / HSECTOR_SIZE); } /* Check if we don't need to create a log file */ if (!Extension) { /* We're done, close handles */ ObDereferenceObject(Event); ZwClose(EventHandle); return STATUS_SUCCESS; } /* Check if we can create the hive */ CreateDisposition = CmpShareSystemHives ? FILE_OPEN : FILE_OPEN_IF; if (*PrimaryDisposition == FILE_CREATED) { /* Over-write the existing log file, since this is a new hive */ CreateDisposition = FILE_SUPERSEDE; } /* Setup the name */ RtlInitUnicodeString(&ExtensionName, Extension); RtlAppendUnicodeStringToString(&FullName, &ExtensionName); /* Initialize the attributes */ InitializeObjectAttributes(&ObjectAttributes, &FullName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL); /* Setup the flags */ IoFlags = FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING; /* Check if this is a log file */ if (!_wcsnicmp(Extension, L".log", 4)) { /* Hide log files */ AttributeFlags |= FILE_ATTRIBUTE_HIDDEN; } /* Now create the file */ Status = ZwCreateFile(Log, DesiredAccess, &ObjectAttributes, &IoStatusBlock, NULL, AttributeFlags, ShareMode, CreateDisposition, IoFlags, NULL, 0); if ((NT_SUCCESS(Status)) && (MarkAsSystemHive)) { /* We opened it, mark it as a system hive */ Status = ZwFsControlFile(*Log, EventHandle, NULL, NULL, &IoStatusBlock, FSCTL_MARK_AS_SYSTEM_HIVE, NULL, 0, NULL, 0); if (Status == STATUS_PENDING) { /* Wait for completion */ KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } /* If we don't support it, ignore the failure */ if (Status == STATUS_INVALID_DEVICE_REQUEST) Status = STATUS_SUCCESS; /* If we failed, close the handle */ if (!NT_SUCCESS(Status)) ZwClose(*Log); } /* Check if anything failed until now */ if (!NT_SUCCESS(Status)) { /* Clear the handle */ *Log = NULL; } else { /* Disable compression */ Status = ZwFsControlFile(*Log, EventHandle, NULL, NULL, &IoStatusBlock, FSCTL_SET_COMPRESSION, &CompressionState, sizeof(CompressionState), NULL, 0); if (Status == STATUS_PENDING) { /* Wait for completion */ KeWaitForSingleObject(Event, Executive, KernelMode, FALSE, NULL); } /* Return the disposition */ *LogDisposition = (ULONG)IoStatusBlock.Information; } /* We're done, close handles and free buffers */ if (NameBuffer) ExFreePoolWithTag(NameBuffer, TAG_CM); ObDereferenceObject(Event); ZwClose(EventHandle); return STATUS_SUCCESS; }
BOOLEAN CmpFileRead ( PHHIVE Hive, ULONG FileType, PULONG FileOffset, PVOID DataBuffer, ULONG DataLength ) /*++ Routine Description: This routine reads in a buffer from a file. It is environment specific. NOTE: We assume the handle is opened for asynchronous access, and that we, and not the IO system, are keeping the offset pointer. NOTE: Only 32bit offsets are supported, even though the underlying IO system on NT supports 64 bit offsets. Arguments: Hive - Hive we are doing I/O for FileType - which supporting file to use FileOffset - pointer to variable providing 32bit offset on input, and receiving new 32bit offset on output. DataBuffer - pointer to buffer DataLength - length of buffer Return Value: FALSE if failure TRUE if success --*/ { NTSTATUS status; LARGE_INTEGER Offset; IO_STATUS_BLOCK IoStatus; PCMHIVE CmHive; HANDLE FileHandle; ULONG LengthToRead; HANDLE eventHandle = NULL; PKEVENT eventObject = NULL; ASSERT(FIELD_OFFSET(CMHIVE, Hive) == 0); CmHive = (PCMHIVE)Hive; FileHandle = CmHive->FileHandles[FileType]; if (FileHandle == NULL) { return TRUE; } CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"CmpFileRead:\n")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"\tHandle=%08lx Offset=%08lx ", FileHandle, *FileOffset)); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_IO,"Buffer=%p Length=%08lx\n", DataBuffer, DataLength)); // // Detect attempt to read off end of 2gig file (this should be irrelevant) // if ((0xffffffff - *FileOffset) < DataLength) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileRead: runoff\n")); return FALSE; } status = CmpCreateEvent( SynchronizationEvent, &eventHandle, &eventObject); if (!NT_SUCCESS(status)) return FALSE; // // We'd really like to just call the filesystems and have them do // the right thing. But the filesystem will attempt to lock our // entire buffer into memory, and that may fail for large requests. // So we split our reads into 64k chunks and call the filesystem for // each one. // ASSERT_PASSIVE_LEVEL(); while (DataLength > 0) { // // Convert ULONG to Large // Offset.LowPart = *FileOffset; Offset.HighPart = 0L; // // trim request down if necessary. // if (DataLength > MAX_FILE_IO) { LengthToRead = MAX_FILE_IO; } else { LengthToRead = DataLength; } status = ZwReadFile( FileHandle, eventHandle, NULL, // apcroutine NULL, // apccontext &IoStatus, DataBuffer, LengthToRead, &Offset, NULL // key ); if (STATUS_PENDING == status) { status = KeWaitForSingleObject(eventObject, Executive, KernelMode, FALSE, NULL); ASSERT(STATUS_SUCCESS == status); status = IoStatus.Status; } // // adjust offsets // *FileOffset = Offset.LowPart + LengthToRead; DataLength -= LengthToRead; DataBuffer = (PVOID)((PCHAR)DataBuffer + LengthToRead); if (NT_SUCCESS(status)) { ASSERT(IoStatus.Status == status); if (IoStatus.Information != LengthToRead) { CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"CmpFileRead:\n\t")); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"Failure1: status = %08lx ", status)); CmKdPrintEx((DPFLTR_CONFIG_ID,CML_BUGCHECK,"IoInformation = %08lx\n", IoStatus.Information)); ObDereferenceObject(eventObject); ZwClose(eventHandle); CmRegistryIODebug.Action = CmpIoFileRead; CmRegistryIODebug.Handle = FileHandle; #if defined(_WIN64) CmRegistryIODebug.Status = (ULONG)IoStatus.Information - LengthToRead; #else CmRegistryIODebug.Status = (ULONG)&IoStatus; #endif return FALSE; } } else { // // set debugging info // CmRegistryIODebug.Action = CmpIoFileRead; CmRegistryIODebug.Handle = FileHandle; CmRegistryIODebug.Status = status; #if DBG DbgPrintEx(DPFLTR_CONFIG_ID,DPFLTR_TRACE_LEVEL,"CmpFileRead:\tFailure2: status = %08lx IoStatus = %08lx\n", status, IoStatus.Status); #endif ObDereferenceObject(eventObject); ZwClose(eventHandle); return FALSE; } } ObDereferenceObject(eventObject); ZwClose(eventHandle); return TRUE; }