441 lines
9.8 KiB
C
441 lines
9.8 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
bootstat.c
|
||
|
||
Abstract:
|
||
|
||
Manipulates the boot status data file.
|
||
|
||
Author:
|
||
|
||
Peter Wieland (peterwie) 01-18-01
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "bldr.h"
|
||
#include "bootstatus.h"
|
||
|
||
#include <stdio.h>
|
||
|
||
#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)}
|
||
|
||
VOID
|
||
BlAutoAdvancedBoot(
|
||
IN OUT PCHAR *LoadOptions,
|
||
IN BSD_LAST_BOOT_STATUS LastBootStatus,
|
||
IN ULONG AdvancedBootMode
|
||
)
|
||
{
|
||
UCHAR bootStatusString[32];
|
||
PUCHAR advancedBootString = NULL;
|
||
|
||
ULONG newLoadOptionsLength;
|
||
PUCHAR newLoadOptions;
|
||
|
||
//
|
||
// Write the last boot status into a string.
|
||
//
|
||
|
||
sprintf(bootStatusString, "LastBootStatus=%d", LastBootStatus);
|
||
|
||
//
|
||
// Based on the advanced boot mode indicated by the caller, adjust the
|
||
// boot options.
|
||
//
|
||
|
||
if (AdvancedBootMode != -1) {
|
||
advancedBootString = BlGetAdvancedBootLoadOptions(AdvancedBootMode);
|
||
}
|
||
|
||
//
|
||
// Determine the length of the new load options string.
|
||
//
|
||
|
||
newLoadOptionsLength = strlen(bootStatusString) + 1;
|
||
|
||
if(*LoadOptions != NULL) {
|
||
newLoadOptionsLength += strlen(*LoadOptions) + 1;
|
||
}
|
||
|
||
if(advancedBootString) {
|
||
newLoadOptionsLength += strlen(advancedBootString) + 1;
|
||
}
|
||
|
||
newLoadOptions = BlAllocateHeap(newLoadOptionsLength * sizeof(UCHAR));
|
||
|
||
if(newLoadOptions == NULL) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Concatenate all the strings together.
|
||
//
|
||
|
||
sprintf(newLoadOptions, "%s %s %s",
|
||
((*LoadOptions != NULL) ? *LoadOptions : ""),
|
||
((advancedBootString != NULL) ? advancedBootString : ""),
|
||
bootStatusString);
|
||
|
||
if(AdvancedBootMode != -1) {
|
||
BlDoAdvancedBootLoadProcessing(AdvancedBootMode);
|
||
}
|
||
|
||
*LoadOptions = newLoadOptions;
|
||
|
||
return;
|
||
}
|
||
|
||
ARC_STATUS
|
||
BlGetSetBootStatusData(
|
||
IN PVOID DataHandle,
|
||
IN BOOLEAN Get,
|
||
IN RTL_BSD_ITEM_TYPE DataItem,
|
||
IN PVOID DataBuffer,
|
||
IN ULONG DataBufferLength,
|
||
OUT PULONG BytesReturned OPTIONAL
|
||
)
|
||
{
|
||
ULONG fileId = (ULONG) ((ULONG_PTR) DataHandle);
|
||
|
||
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)
|
||
};
|
||
|
||
ULONG dataFileVersion;
|
||
|
||
LARGE_INTEGER fileOffset;
|
||
ULONG itemLength;
|
||
|
||
ULONG bytesRead;
|
||
|
||
ARC_STATUS status;
|
||
|
||
ASSERT(RtlBsdItemMax == (sizeof(bootStatusFields) / sizeof(bootStatusFields[0])));
|
||
|
||
//
|
||
// Read the version number out of the data file.
|
||
//
|
||
|
||
fileOffset.QuadPart = 0;
|
||
|
||
status = BlSeek(fileId, &fileOffset, SeekAbsolute);
|
||
|
||
if(status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
status = BlRead(fileId,
|
||
&dataFileVersion,
|
||
sizeof(ULONG),
|
||
&bytesRead);
|
||
|
||
if(status != ESUCCESS) {
|
||
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 EINVAL;
|
||
}
|
||
|
||
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 EINVAL;
|
||
}
|
||
|
||
status = BlSeek(fileId, &fileOffset, SeekAbsolute);
|
||
|
||
if(status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
if(Get) {
|
||
status = BlRead(fileId,
|
||
DataBuffer,
|
||
itemLength,
|
||
&bytesRead);
|
||
|
||
} else {
|
||
status = BlWrite(fileId,
|
||
DataBuffer,
|
||
itemLength,
|
||
&bytesRead);
|
||
}
|
||
|
||
if((status == ESUCCESS) && ARGUMENT_PRESENT(BytesReturned)) {
|
||
*BytesReturned = bytesRead;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
ARC_STATUS
|
||
BlLockBootStatusData(
|
||
IN ULONG SystemPartitionId,
|
||
IN PCHAR SystemPartition,
|
||
IN PCHAR SystemDirectory,
|
||
OUT PVOID *DataHandle
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine opens the boot status data file.
|
||
|
||
Arguments:
|
||
|
||
SystemPartitionId - if non-zero this is the arc file id of the system
|
||
partition. This will be used to locate the system
|
||
directory instead of the system partition name (below).
|
||
|
||
SystemPartition - the arc name of the system partition. Ignored if
|
||
SystemPartitionId is non-zero.
|
||
|
||
SystemDirectory - the name of the system directory.
|
||
|
||
DataHandle - returns a handle to the boot status data.
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS if the status data could be locked, or error indicating why not.
|
||
|
||
--*/
|
||
{
|
||
ULONG driveId;
|
||
|
||
UCHAR filePath[100];
|
||
ULONG fileId;
|
||
|
||
ARC_STATUS status;
|
||
|
||
if(SystemPartitionId == 0) {
|
||
|
||
//
|
||
// Attempt to open the system partition
|
||
//
|
||
|
||
status = ArcOpen(SystemPartition, ArcOpenReadWrite, &driveId);
|
||
|
||
if(status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
} else {
|
||
driveId = SystemPartitionId;
|
||
}
|
||
|
||
//
|
||
// Now attempt to open the file <SystemDirectory>\bootstat.dat
|
||
//
|
||
|
||
strcpy(filePath, SystemDirectory);
|
||
strcat(filePath, BSD_FILE_NAME);
|
||
|
||
status = BlOpen(driveId, filePath, ArcOpenReadWrite, &fileId);
|
||
|
||
if(SystemPartitionId == 0) {
|
||
//
|
||
// Close the drive.
|
||
//
|
||
|
||
ArcClose(driveId);
|
||
}
|
||
|
||
//
|
||
// The file doesn't exist so we don't know the state of the last boot.
|
||
//
|
||
|
||
if(status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
*DataHandle = (PVOID) ((ULONG_PTR) fileId);
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
BlUnlockBootStatusData(
|
||
IN PVOID DataHandle
|
||
)
|
||
{
|
||
ULONG fileId = (ULONG) ((ULONG_PTR) DataHandle);
|
||
|
||
BlClose(fileId);
|
||
return;
|
||
}
|
||
|
||
ULONG
|
||
BlGetLastBootStatus(
|
||
IN PVOID DataHandle,
|
||
OUT BSD_LAST_BOOT_STATUS *LastBootStatus
|
||
)
|
||
{
|
||
UCHAR lastBootGood;
|
||
UCHAR lastShutdownGood;
|
||
UCHAR aabEnabled;
|
||
|
||
ULONG advancedBootMode = -1;
|
||
|
||
ARC_STATUS status;
|
||
|
||
*LastBootStatus = BsdLastBootGood;
|
||
|
||
//
|
||
// The file contains a simple data structure so i can avoid parsing an
|
||
// INI file. If this proves to be insufficient for policy management then
|
||
// we'll change it into an ini file.
|
||
//
|
||
|
||
//
|
||
// Read the last boot status.
|
||
//
|
||
|
||
status = BlGetSetBootStatusData(DataHandle,
|
||
TRUE,
|
||
RtlBsdItemBootGood,
|
||
&lastBootGood,
|
||
sizeof(UCHAR),
|
||
NULL);
|
||
|
||
if(status != ESUCCESS) {
|
||
*LastBootStatus = BsdLastBootUnknown;
|
||
return advancedBootMode;
|
||
}
|
||
|
||
status = BlGetSetBootStatusData(DataHandle,
|
||
TRUE,
|
||
RtlBsdItemBootShutdown,
|
||
&lastShutdownGood,
|
||
sizeof(UCHAR),
|
||
NULL);
|
||
|
||
if(status != ESUCCESS) {
|
||
*LastBootStatus = BsdLastBootUnknown;
|
||
return advancedBootMode;
|
||
}
|
||
|
||
status = BlGetSetBootStatusData(DataHandle,
|
||
TRUE,
|
||
RtlBsdItemAabEnabled,
|
||
&aabEnabled,
|
||
sizeof(UCHAR),
|
||
NULL);
|
||
|
||
if(status != ESUCCESS) {
|
||
*LastBootStatus = BsdLastBootUnknown;
|
||
return advancedBootMode;
|
||
}
|
||
|
||
//
|
||
// If the system was shutdown cleanly then don't bother to check if the
|
||
// boot was good.
|
||
//
|
||
|
||
if(lastShutdownGood) {
|
||
return advancedBootMode;
|
||
}
|
||
|
||
//
|
||
// Determine the last boot status & what action to take.
|
||
//
|
||
|
||
if(lastBootGood == FALSE) {
|
||
|
||
//
|
||
// Enable last known good.
|
||
//
|
||
|
||
advancedBootMode = 6;
|
||
*LastBootStatus = BsdLastBootFailed;
|
||
} else if(lastShutdownGood == FALSE) {
|
||
|
||
//
|
||
// Enable safe mode without networking.
|
||
//
|
||
|
||
advancedBootMode = 0;
|
||
*LastBootStatus = BsdLastBootNotShutdown;
|
||
}
|
||
|
||
//
|
||
// Now disable auto safemode actions if requested.
|
||
//
|
||
|
||
if(aabEnabled == FALSE) {
|
||
advancedBootMode = -1;
|
||
}
|
||
|
||
return advancedBootMode;
|
||
}
|
||
|
||
VOID
|
||
BlWriteBootStatusFlags(
|
||
IN ULONG SystemPartitionId,
|
||
IN PUCHAR SystemDirectory,
|
||
IN BOOLEAN LastBootSucceeded,
|
||
IN BOOLEAN LastBootShutdown
|
||
)
|
||
{
|
||
PVOID dataHandle;
|
||
|
||
ARC_STATUS status;
|
||
|
||
status = BlLockBootStatusData(SystemPartitionId,
|
||
NULL,
|
||
SystemDirectory,
|
||
&dataHandle);
|
||
|
||
if(status == ESUCCESS) {
|
||
|
||
BlGetSetBootStatusData(dataHandle,
|
||
FALSE,
|
||
RtlBsdItemBootGood,
|
||
&LastBootSucceeded,
|
||
sizeof(UCHAR),
|
||
NULL);
|
||
|
||
BlGetSetBootStatusData(dataHandle,
|
||
FALSE,
|
||
RtlBsdItemBootShutdown,
|
||
&LastBootShutdown,
|
||
sizeof(UCHAR),
|
||
NULL);
|
||
|
||
BlUnlockBootStatusData(dataHandle);
|
||
}
|
||
|
||
return;
|
||
}
|