375 lines
9.4 KiB
C
375 lines
9.4 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 2001 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
bootstatus.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code for manipulating the boot status file.
|
|||
|
|
|||
|
The boot status file has some odd requirements and needs to be accessed/
|
|||
|
modified both by kernel and user-mode code.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntrtlp.h"
|
|||
|
// #include <nt.h>
|
|||
|
// #include <ntrtl.h>
|
|||
|
// #include <zwapi.h>
|
|||
|
|
|||
|
#define BSD_UNICODE 1
|
|||
|
#include "bootstatus.h"
|
|||
|
|
|||
|
#if defined(ALLOC_PRAGMA) && defined(NTOS_KERNEL_RUNTIME)
|
|||
|
#pragma alloc_text(PAGE,RtlLockBootStatusData)
|
|||
|
#pragma alloc_text(PAGE,RtlUnlockBootStatusData)
|
|||
|
#pragma alloc_text(PAGE,RtlGetSetBootStatusData)
|
|||
|
#pragma alloc_text(PAGE,RtlCreateBootStatusDataFile)
|
|||
|
#endif
|
|||
|
|
|||
|
#define MYTAG 'fdsb' // bsdf
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RtlLockBootStatusData(
|
|||
|
OUT PHANDLE BootStatusDataHandle
|
|||
|
)
|
|||
|
{
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
|
|||
|
WCHAR fileNameBuffer[MAXIMUM_FILENAME_LENGTH+1];
|
|||
|
UNICODE_STRING fileName;
|
|||
|
|
|||
|
HANDLE dataFileHandle;
|
|||
|
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
wcsncpy(fileNameBuffer, L"\\SystemRoot", MAXIMUM_FILENAME_LENGTH);
|
|||
|
wcsncat(fileNameBuffer,
|
|||
|
BSD_FILE_NAME,
|
|||
|
MAXIMUM_FILENAME_LENGTH - wcslen(fileNameBuffer));
|
|||
|
|
|||
|
RtlInitUnicodeString(&fileName, fileNameBuffer);
|
|||
|
|
|||
|
InitializeObjectAttributes(&objectAttributes,
|
|||
|
&fileName,
|
|||
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
status = ZwOpenFile(&dataFileHandle,
|
|||
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|||
|
&objectAttributes,
|
|||
|
&ioStatusBlock,
|
|||
|
0,
|
|||
|
FILE_SYNCHRONOUS_IO_NONALERT);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
if(NT_SUCCESS(status)) {
|
|||
|
*BootStatusDataHandle = dataFileHandle;
|
|||
|
} else {
|
|||
|
*BootStatusDataHandle = NULL;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RtlUnlockBootStatusData(
|
|||
|
IN HANDLE BootStatusDataHandle
|
|||
|
)
|
|||
|
{
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
USHORT i = COMPRESSION_FORMAT_NONE;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
//
|
|||
|
// Decompress the data file. If the file is not already compressed then
|
|||
|
// this should be a very lightweight operation (so say the FS guys).
|
|||
|
//
|
|||
|
// On the other hand if the file is compressed then the boot loader will
|
|||
|
// be unable to write to it and auto-recovery is effectively disabled.
|
|||
|
//
|
|||
|
|
|||
|
status = ZwFsControlFile(
|
|||
|
BootStatusDataHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
FSCTL_SET_COMPRESSION,
|
|||
|
&i,
|
|||
|
sizeof(USHORT),
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
status = ZwFlushBuffersFile(BootStatusDataHandle, &ioStatusBlock);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
ZwClose(BootStatusDataHandle);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#define FIELD_SIZE(type, field) sizeof(((type *)0)->field)
|
|||
|
#define FIELD_OFFSET_AND_SIZE(n) {FIELD_OFFSET(BSD_BOOT_STATUS_DATA, n), FIELD_SIZE(BSD_BOOT_STATUS_DATA, n)}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RtlGetSetBootStatusData(
|
|||
|
IN HANDLE Handle,
|
|||
|
IN BOOLEAN Get,
|
|||
|
IN RTL_BSD_ITEM_TYPE DataItem,
|
|||
|
IN PVOID DataBuffer,
|
|||
|
IN ULONG DataBufferLength,
|
|||
|
OUT PULONG BytesReturned OPTIONAL
|
|||
|
)
|
|||
|
{
|
|||
|
struct {
|
|||
|
ULONG FieldOffset;
|
|||
|
ULONG FieldLength;
|
|||
|
} bootStatusFields[] = {
|
|||
|
FIELD_OFFSET_AND_SIZE(Version),
|
|||
|
FIELD_OFFSET_AND_SIZE(ProductType),
|
|||
|
FIELD_OFFSET_AND_SIZE(AutoAdvancedBoot),
|
|||
|
FIELD_OFFSET_AND_SIZE(AdvancedBootMenuTimeout),
|
|||
|
FIELD_OFFSET_AND_SIZE(LastBootSucceeded),
|
|||
|
FIELD_OFFSET_AND_SIZE(LastBootShutdown)
|
|||
|
};
|
|||
|
|
|||
|
LARGE_INTEGER fileOffset;
|
|||
|
ULONG dataFileVersion;
|
|||
|
|
|||
|
ULONG itemLength;
|
|||
|
|
|||
|
ULONG bytesRead;
|
|||
|
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
ASSERT(RtlBsdItemMax == (sizeof(bootStatusFields) / sizeof(bootStatusFields[0])));
|
|||
|
|
|||
|
//
|
|||
|
// Read the version number out of the data file.
|
|||
|
//
|
|||
|
|
|||
|
fileOffset.QuadPart = 0;
|
|||
|
|
|||
|
status = ZwReadFile(Handle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
&dataFileVersion,
|
|||
|
sizeof(ULONG),
|
|||
|
&fileOffset,
|
|||
|
NULL);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
if(!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the data item requsted isn't one we have code to handle then
|
|||
|
// return invalid parameter.
|
|||
|
//
|
|||
|
|
|||
|
if(DataItem >= (sizeof(bootStatusFields) / sizeof(bootStatusFields[0]))) {
|
|||
|
return STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
fileOffset.QuadPart = bootStatusFields[DataItem].FieldOffset;
|
|||
|
itemLength = bootStatusFields[DataItem].FieldLength;
|
|||
|
|
|||
|
//
|
|||
|
// If the data item offset is beyond the end of the file then return a
|
|||
|
// versioning error.
|
|||
|
//
|
|||
|
|
|||
|
if((fileOffset.QuadPart + itemLength) > dataFileVersion) {
|
|||
|
return STATUS_REVISION_MISMATCH;
|
|||
|
}
|
|||
|
|
|||
|
if(DataBufferLength < itemLength) {
|
|||
|
DataBufferLength = itemLength;
|
|||
|
return STATUS_BUFFER_TOO_SMALL;
|
|||
|
}
|
|||
|
|
|||
|
if(Get) {
|
|||
|
status = ZwReadFile(Handle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
DataBuffer,
|
|||
|
itemLength,
|
|||
|
&fileOffset,
|
|||
|
NULL);
|
|||
|
} else {
|
|||
|
status = ZwWriteFile(Handle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
DataBuffer,
|
|||
|
itemLength,
|
|||
|
&fileOffset,
|
|||
|
NULL);
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
if(NT_SUCCESS(status) && ARGUMENT_PRESENT(BytesReturned)) {
|
|||
|
*BytesReturned = (ULONG) ioStatusBlock.Information;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RtlCreateBootStatusDataFile(
|
|||
|
VOID
|
|||
|
)
|
|||
|
{
|
|||
|
OBJECT_ATTRIBUTES objectAttributes;
|
|||
|
|
|||
|
WCHAR fileNameBuffer[MAXIMUM_FILENAME_LENGTH+1];
|
|||
|
UNICODE_STRING fileName;
|
|||
|
|
|||
|
HANDLE dataFileHandle;
|
|||
|
|
|||
|
IO_STATUS_BLOCK ioStatusBlock;
|
|||
|
|
|||
|
LARGE_INTEGER t;
|
|||
|
UCHAR zero = 0;
|
|||
|
|
|||
|
BSD_BOOT_STATUS_DATA defaultValues;
|
|||
|
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
wcsncpy(fileNameBuffer, L"\\SystemRoot", MAXIMUM_FILENAME_LENGTH);
|
|||
|
wcsncat(fileNameBuffer,
|
|||
|
BSD_FILE_NAME,
|
|||
|
MAXIMUM_FILENAME_LENGTH - wcslen(fileNameBuffer));
|
|||
|
|
|||
|
RtlInitUnicodeString(&fileName, fileNameBuffer);
|
|||
|
|
|||
|
InitializeObjectAttributes(&objectAttributes,
|
|||
|
&fileName,
|
|||
|
OBJ_CASE_INSENSITIVE,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
//
|
|||
|
// The file must be large enough that it doesn't reside in the MFT entry
|
|||
|
// or the loader won't be able to write to it.
|
|||
|
//
|
|||
|
|
|||
|
t.QuadPart = 2048;
|
|||
|
|
|||
|
//
|
|||
|
// Create the file.
|
|||
|
//
|
|||
|
|
|||
|
status = ZwCreateFile(&dataFileHandle,
|
|||
|
FILE_GENERIC_READ | FILE_GENERIC_WRITE,
|
|||
|
&objectAttributes,
|
|||
|
&(ioStatusBlock),
|
|||
|
&t,
|
|||
|
FILE_ATTRIBUTE_SYSTEM,
|
|||
|
0,
|
|||
|
FILE_CREATE,
|
|||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
|||
|
NULL,
|
|||
|
0);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
if(!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Write a single zero byte to the 0x7ffth byte in the file to make
|
|||
|
// sure that 2k are actually allocated. This is to ensure that the
|
|||
|
// file will not become attribute resident even after a conversion
|
|||
|
// from FAT to NTFS.
|
|||
|
//
|
|||
|
|
|||
|
t.QuadPart = t.QuadPart - 1;
|
|||
|
status = ZwWriteFile(dataFileHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
&zero,
|
|||
|
1,
|
|||
|
&t,
|
|||
|
NULL);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
if(!NT_SUCCESS(status)) {
|
|||
|
goto CreateDone;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now write out the default values to the beginning of the file.
|
|||
|
//
|
|||
|
|
|||
|
defaultValues.Version = sizeof(BSD_BOOT_STATUS_DATA);
|
|||
|
RtlGetNtProductType(&(defaultValues.ProductType));
|
|||
|
defaultValues.AutoAdvancedBoot = FALSE;
|
|||
|
defaultValues.AdvancedBootMenuTimeout = 30;
|
|||
|
defaultValues.LastBootSucceeded = TRUE;
|
|||
|
defaultValues.LastBootShutdown = FALSE;
|
|||
|
|
|||
|
t.QuadPart = 0;
|
|||
|
|
|||
|
status = ZwWriteFile(dataFileHandle,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
&ioStatusBlock,
|
|||
|
&defaultValues,
|
|||
|
sizeof(BSD_BOOT_STATUS_DATA),
|
|||
|
&t,
|
|||
|
NULL);
|
|||
|
|
|||
|
ASSERT(status != STATUS_PENDING);
|
|||
|
|
|||
|
if(!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// The data file was created and we can assume the contents were zeroed
|
|||
|
// even if we couldn't write out the defaults. Since this wouldn't
|
|||
|
// enable auto-advanced boot we'll leave the data file in place with
|
|||
|
// its zeroed contents.
|
|||
|
//
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
CreateDone:
|
|||
|
|
|||
|
ZwClose(dataFileHandle);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|