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