/*++ 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 #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 ) { CHAR bootStatusString[32]; PCHAR advancedBootString = NULL; ULONG newLoadOptionsLength; PCHAR 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 = (ULONG)strlen(bootStatusString) + 1; if(*LoadOptions != NULL) { newLoadOptionsLength += (ULONG)strlen(*LoadOptions) + 1; } if(advancedBootString) { newLoadOptionsLength += (ULONG)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 EINVAL; } 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; CHAR 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 \bootstat.dat // if (sizeof(filePath) < strlen(SystemDirectory) + strlen(BSD_FILE_NAME) + 1) { return ENOMEM; } 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 = (ULONG)-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 = (ULONG)-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, (PCHAR)SystemDirectory, &dataHandle); if(status == ESUCCESS) { BlGetSetBootStatusData(dataHandle, FALSE, RtlBsdItemBootGood, &LastBootSucceeded, sizeof(UCHAR), NULL); BlGetSetBootStatusData(dataHandle, FALSE, RtlBsdItemBootShutdown, &LastBootShutdown, sizeof(UCHAR), NULL); BlUnlockBootStatusData(dataHandle); } return; }