X-Git-Url: https://code.delx.au/refind/blobdiff_plain/45819ae8739dea4a9710626b5817313e63b07c1a..c8883302f06e69a8b961280b73e29b4b09cacb9d:/refind/lib.c diff --git a/refind/lib.c b/refind/lib.c index 12b5ce3..19e140a 100644 --- a/refind/lib.c +++ b/refind/lib.c @@ -34,7 +34,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - * Modifications copyright (c) 2012-2014 Roderick W. Smith + * Modifications copyright (c) 2012-2015 Roderick W. Smith * * Modifications distributed under the terms of the GNU General Public * License (GPL) version 3 (GPLv3), a copy of which must be distributed @@ -49,6 +49,7 @@ #include "../include/refit_call_wrapper.h" #include "../include/RemovableMedia.h" #include "gpt.h" +#include "config.h" #ifdef __MAKEWITH_GNUEFI #define EfiReallocatePool ReallocatePool @@ -74,6 +75,7 @@ EFI_DEVICE_PATH EndDevicePath[] = { #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" #define BTRFS_SIGNATURE "_BHRfS_M" +#define NTFS_SIGNATURE "NTFS " // variables @@ -265,6 +267,43 @@ static EFI_STATUS FinishInitRefitLib(VOID) return EFI_SUCCESS; } +// +// EFI variable read and write functions +// + +// From gummiboot: Retrieve a raw EFI variable. +// Returns EFI status +EFI_STATUS EfivarGetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 **buffer, UINTN *size) { + CHAR8 *buf; + UINTN l; + EFI_STATUS err; + + l = sizeof(CHAR16 *) * EFI_MAXIMUM_VARIABLE_SIZE; + buf = AllocatePool(l); + if (!buf) + return EFI_OUT_OF_RESOURCES; + + err = refit_call5_wrapper(RT->GetVariable, name, vendor, NULL, &l, buf); + if (EFI_ERROR(err) == EFI_SUCCESS) { + *buffer = buf; + if (size) + *size = l; + } else + MyFreePool(buf); + return err; +} // EFI_STATUS EfivarGetRaw() + +// From gummiboot: Set an EFI variable +EFI_STATUS EfivarSetRaw(EFI_GUID *vendor, CHAR16 *name, CHAR8 *buf, UINTN size, BOOLEAN persistent) { + UINT32 flags; + + flags = EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS; + if (persistent) + flags |= EFI_VARIABLE_NON_VOLATILE; + + return refit_call5_wrapper(RT->SetVariable, name, vendor, flags, size, buf); +} // EFI_STATUS EfivarSetRaw() + // // list functions // @@ -397,6 +436,9 @@ static CHAR16 *FSTypeName(IN UINT32 TypeCode) { CHAR16 *retval = NULL; switch (TypeCode) { + case FS_TYPE_WHOLEDISK: + retval = L" whole disk"; + break; case FS_TYPE_FAT: retval = L" FAT"; break; @@ -421,6 +463,9 @@ static CHAR16 *FSTypeName(IN UINT32 TypeCode) { case FS_TYPE_ISO9660: retval = L" ISO-9660"; break; + case FS_TYPE_NTFS: + retval = L" NTFS"; + break; default: retval = L""; break; @@ -429,31 +474,52 @@ static CHAR16 *FSTypeName(IN UINT32 TypeCode) { } // CHAR16 *FSTypeName() // Identify the filesystem type and record the filesystem's UUID/serial number, -// if possible. Expects a Buffer containing the first few (normally 4096) bytes -// of the filesystem. Sets the filesystem type code in Volume->FSType and the -// UUID/serial number in Volume->VolUuid. Note that the UUID value is recognized -// differently for each filesystem, and is currently supported only for -// ext2/3/4fs and ReiserFS. If the UUID can't be determined, it's set to 0. Also, the UUID -// is just read directly into memory; it is *NOT* valid when displayed by -// GuidAsString() or used in other GUID/UUID-manipulating functions. (As I -// write, it's being used merely to detect partitions that are part of a -// RAID 1 array.) +// if possible. Expects a Buffer containing the first few (normally at least +// 4096) bytes of the filesystem. Sets the filesystem type code in Volume->FSType +// and the UUID/serial number in Volume->VolUuid. Note that the UUID value is +// recognized differently for each filesystem, and is currently supported only +// for NTFS, ext2/3/4fs, and ReiserFS (and for NTFS it's really a 64-bit serial +// number not a UUID or GUID). If the UUID can't be determined, it's set to 0. +// Also, the UUID is just read directly into memory; it is *NOT* valid when +// displayed by GuidAsString() or used in other GUID/UUID-manipulating +// functions. (As I write, it's being used merely to detect partitions that are +// part of a RAID 1 array.) static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFIT_VOLUME *Volume) { UINT32 *Ext2Incompat, *Ext2Compat; UINT16 *Magic16; char *MagicString; + EFI_FILE *RootDir; if ((Buffer != NULL) && (Volume != NULL)) { SetMem(&(Volume->VolUuid), sizeof(EFI_GUID), 0); Volume->FSType = FS_TYPE_UNKNOWN; if (BufferSize >= 512) { + + // Search for NTFS, FAT, and MBR/EBR. + // These all have 0xAA55 at the end of the first sector, but FAT and + // MBR/EBR are not easily distinguished. Thus, we first check to see + // if the "volume" is in fact a disk device; then look for NTFS + // "magic"; and then check to see if the volume can be mounted, thus + // relying on the EFI's built-in FAT driver to identify FAT. Magic16 = (UINT16*) (Buffer + 510); if (*Magic16 == FAT_MAGIC) { - Volume->FSType = FS_TYPE_FAT; + MagicString = (char*) (Buffer + 3); + // Confusingly, "LogicalPartition" refers to the presence of a + // partition table, not an MBR logical partition. + if (Volume->BlockIO->Media->LogicalPartition) { + Volume->FSType = FS_TYPE_WHOLEDISK; + } else if (CompareMem(MagicString, NTFS_SIGNATURE, 8) == 0) { + Volume->FSType = FS_TYPE_NTFS; + CopyMem(&(Volume->VolUuid), Buffer + 0x48, sizeof(UINT64)); + } else { + RootDir = LibOpenRoot(Volume->DeviceHandle); + if (RootDir != NULL) + Volume->FSType = FS_TYPE_FAT; + } // if/elseif/else return; } // if - } // search for FAT magic + } // search for FAT and NTFS magic if (BufferSize >= (1024 + 100)) { Magic16 = (UINT16*) (Buffer + 1024 + 56); @@ -498,6 +564,7 @@ static VOID SetFilesystemData(IN UINT8 *Buffer, IN UINTN BufferSize, IN OUT REFI return; } } // search for HFS+ magic + } // if (Buffer != NULL) } // UINT32 SetFilesystemData() @@ -526,7 +593,11 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->BlockIOOffset, SAMPLE_SIZE, Buffer); if (!EFI_ERROR(Status)) { - SetFilesystemData(Buffer, SAMPLE_SIZE, Volume); +// if (Volume->BlockIO->Media->LogicalPartition) +// Print(L"Skipping; whole disk!\n"); +// else + SetFilesystemData(Buffer, SAMPLE_SIZE, Volume); +// PauseForKey(); if ((*((UINT16 *)(Buffer + 510)) == 0xaa55 && Buffer[0] != 0) && (FindMem(Buffer, 512, "EXFAT", 5) == -1)) { *Bootable = TRUE; Volume->HasBootCode = TRUE; @@ -563,6 +634,15 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->OSIconName = L"freebsd"; Volume->OSName = L"FreeBSD"; + // If more differentiation needed, also search for + // "Invalid partition table" &/or "Missing boot loader". + } else if ((*((UINT16 *)(Buffer + 510)) == 0xaa55) && + (FindMem(Buffer, SECTOR_SIZE, "Boot loader too large", 21) >= 0) && + (FindMem(Buffer, SECTOR_SIZE, "I/O error loading boot loader", 29) >= 0)) { + Volume->HasBootCode = TRUE; + Volume->OSIconName = L"freebsd"; + Volume->OSName = L"FreeBSD"; + } else if (FindMem(Buffer, 512, "!Loading", 8) >= 0 || FindMem(Buffer, SECTOR_SIZE, "/cdboot\0/CDBOOT\0", 16) >= 0) { Volume->HasBootCode = TRUE; @@ -575,14 +655,16 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) Volume->OSIconName = L"netbsd"; Volume->OSName = L"NetBSD"; + // Windows NT/200x/XP } else if (FindMem(Buffer, SECTOR_SIZE, "NTLDR", 5) >= 0) { Volume->HasBootCode = TRUE; Volume->OSIconName = L"win"; Volume->OSName = L"Windows"; + // Windows Vista/7/8 } else if (FindMem(Buffer, SECTOR_SIZE, "BOOTMGR", 7) >= 0) { Volume->HasBootCode = TRUE; - Volume->OSIconName = L"winvista,win"; + Volume->OSIconName = L"win8,win"; Volume->OSName = L"Windows"; } else if (FindMem(Buffer, 512, "CPUBOOT SYS", 11) >= 0 || @@ -616,7 +698,7 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) } // NOTE: If you add an operating system with a name that starts with 'W' or 'L', you - // need to fix AddLegacyEntry in main.c. + // need to fix AddLegacyEntry in refind/legacy.c. #if REFIT_DEBUG > 0 Print(L" Result of bootcode detection: %s %s (%s)\n", @@ -661,6 +743,9 @@ static VOID ScanVolumeBootcode(REFIT_VOLUME *Volume, BOOLEAN *Bootable) // Set default volume badge icon based on /.VolumeBadge.{icns|png} file or disk kind VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) { + if (GlobalConfig.HideUIFlags & HIDEUI_FLAG_BADGES) + return; + if (Volume->VolBadgeImage == NULL) { Volume->VolBadgeImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeBadge", GlobalConfig.IconSizes[ICON_SIZE_BADGE]); } @@ -676,6 +761,9 @@ VOID SetVolumeBadgeIcon(REFIT_VOLUME *Volume) case DISK_KIND_OPTICAL: Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_OPTICAL); break; + case DISK_KIND_NET: + Volume->VolBadgeImage = BuiltinIcon(BUILTIN_ICON_VOL_NET); + break; } // switch() } } // VOID SetVolumeBadgeIcon() @@ -708,48 +796,44 @@ static CHAR16 *SizeInIEEEUnits(UINT64 SizeInBytes) { } // CHAR16 *SizeInIEEEUnits() // Return a name for the volume. Ideally this should be the label for the -// filesystem it contains, but this function falls back to describing the +// filesystem or volume, but this function falls back to describing the // filesystem by size (200 MiB, etc.) and/or type (ext2, HFS+, etc.), if // this information can be extracted. // The calling function is responsible for freeing the memory allocated // for the name string. static CHAR16 *GetVolumeName(REFIT_VOLUME *Volume) { - EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr; + EFI_FILE_SYSTEM_INFO *FileSystemInfoPtr = NULL; CHAR16 *FoundName = NULL; CHAR16 *SISize, *TypeName; - FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); - if (FileSystemInfoPtr != NULL) { // we have filesystem information (size, label).... - if ((FileSystemInfoPtr->VolumeLabel != NULL) && (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { - FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); - } - - // Special case: Old versions of the rEFInd HFS+ driver always returns label of "HFS+ volume", so wipe - // this so that we can build a new name that includes the size.... - if ((FoundName != NULL) && (StrCmp(FoundName, L"HFS+ volume") == 0) && (Volume->FSType == FS_TYPE_HFSPLUS)) { - MyFreePool(FoundName); - FoundName = NULL; - } // if rEFInd HFS+ driver suspected - - // If no filesystem name, try to use the partition name.... - if ((FoundName == NULL) && (Volume->PartName != NULL) && (StrLen(Volume->PartName) > 0) && - !IsIn(Volume->PartName, IGNORE_PARTITION_NAMES)) { - FoundName = StrDuplicate(Volume->PartName); - } // if use partition name - - // No filesystem or acceptable partition name, so use fs type and size - if (FoundName == NULL) { - FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); - if (FoundName != NULL) { - SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize); - SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType)); - MyFreePool(SISize); - } // if allocated memory OK - } // if (FoundName == NULL) - - FreePool(FileSystemInfoPtr); - - } else { // fs driver not returning info; fall back on our own information.... + if (Volume->RootDir != NULL) { + FileSystemInfoPtr = LibFileSystemInfo(Volume->RootDir); + } + + if ((FileSystemInfoPtr != NULL) && (FileSystemInfoPtr->VolumeLabel != NULL) && + (StrLen(FileSystemInfoPtr->VolumeLabel) > 0)) { + FoundName = StrDuplicate(FileSystemInfoPtr->VolumeLabel); + } + + // If no filesystem name, try to use the partition name.... + if ((FoundName == NULL) && (Volume->PartName != NULL) && (StrLen(Volume->PartName) > 0) && + !IsIn(Volume->PartName, IGNORE_PARTITION_NAMES)) { + FoundName = StrDuplicate(Volume->PartName); + } // if use partition name + + // No filesystem or acceptable partition name, so use fs type and size + if ((FoundName == NULL) && (FileSystemInfoPtr != NULL)) { + FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); + if (FoundName != NULL) { + SISize = SizeInIEEEUnits(FileSystemInfoPtr->VolumeSize); + SPrint(FoundName, 255, L"%s%s volume", SISize, FSTypeName(Volume->FSType)); + MyFreePool(SISize); + } // if allocated memory OK + } // if (FoundName == NULL) + + MyFreePool(FileSystemInfoPtr); + + if (FoundName == NULL) { FoundName = AllocateZeroPool(sizeof(CHAR16) * 256); if (FoundName != NULL) { TypeName = FSTypeName(Volume->FSType); // NOTE: Don't free TypeName; function returns constant @@ -762,7 +846,6 @@ static CHAR16 *GetVolumeName(REFIT_VOLUME *Volume) { // TODO: Above could be improved/extended, in case filesystem name is not found, // such as: - // - use partition label // - use or add disk/partition number (e.g., "(hd0,2)") // Desperate fallback name.... @@ -772,7 +855,7 @@ static CHAR16 *GetVolumeName(REFIT_VOLUME *Volume) { return FoundName; } // static CHAR16 *GetVolumeName() -// Determine the unique GUID of the volume and store it. +// Determine the unique GUID and name of the volume and store them. static VOID SetPartGuidAndName(REFIT_VOLUME *Volume, EFI_DEVICE_PATH_PROTOCOL *DevicePath) { HARDDRIVE_DEVICE_PATH *HdDevicePath; @@ -788,6 +871,21 @@ static VOID SetPartGuidAndName(REFIT_VOLUME *Volume, EFI_DEVICE_PATH_PROTOCOL *D } // if } // VOID SetPartGuid() +// Return TRUE if NTFS boot files are found or if Volume is unreadable, +// FALSE otherwise. The idea is to weed out non-boot NTFS volumes from +// BIOS/legacy boot list on Macs. We can't assume NTFS will be readable, +// so return TRUE if it's unreadable; but if it IS readable, return +// TRUE only if Windows boot files are found. +static BOOLEAN HasWindowsBiosBootFiles(REFIT_VOLUME *Volume) { + BOOLEAN FilesFound = TRUE; + + if (Volume->RootDir != NULL) { + FilesFound = FileExists(Volume->RootDir, L"NTLDR") || // Windows NT/200x/XP boot file + FileExists(Volume->RootDir, L"bootmgr"); // Windows Vista/7/8 boot file + } // if + return FilesFound; +} // static VOID HasWindowsBiosBootFiles() + VOID ScanVolume(REFIT_VOLUME *Volume) { EFI_STATUS Status; @@ -891,32 +989,38 @@ VOID ScanVolume(REFIT_VOLUME *Volume) DevicePath = NextDevicePath; } // while - if (!Bootable) { + if (!Bootable) { #if REFIT_DEBUG > 0 - if (Volume->HasBootCode) - Print(L" Volume considered non-bootable, but boot code is present\n"); + if (Volume->HasBootCode) + Print(L" Volume considered non-bootable, but boot code is present\n"); #endif - Volume->HasBootCode = FALSE; - } + Volume->HasBootCode = FALSE; + } - // open the root directory of the volume - Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); + // open the root directory of the volume + Volume->RootDir = LibOpenRoot(Volume->DeviceHandle); - // Set volume icon based on .VolumeBadge icon or disk kind - SetVolumeBadgeIcon(Volume); + // Set volume icon based on .VolumeBadge icon or disk kind + SetVolumeBadgeIcon(Volume); - if (Volume->RootDir == NULL) { - Volume->IsReadable = FALSE; - return; - } else { - Volume->IsReadable = TRUE; - } + Volume->VolName = GetVolumeName(Volume); - Volume->VolName = GetVolumeName(Volume); + if (Volume->RootDir == NULL) { + Volume->IsReadable = FALSE; + return; + } else { + Volume->IsReadable = TRUE; + if ((GlobalConfig.LegacyType == LEGACY_TYPE_MAC) && (Volume->FSType == FS_TYPE_NTFS) && Volume->HasBootCode) { + // VBR boot code found on NTFS, but volume is not actually bootable + // unless there are actual boot file, so check for them.... + Volume->HasBootCode = HasWindowsBiosBootFiles(Volume); + } + } // if/else - // get custom volume icons if present - if (!Volume->VolIconImage) - Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]); + // get custom volume icons if present + if (!Volume->VolIconImage) { + Volume->VolIconImage = egLoadIconAnyType(Volume->RootDir, L"", L".VolumeIcon", GlobalConfig.IconSizes[ICON_SIZE_BIG]); + } } // ScanVolume() static VOID ScanExtendedPartition(REFIT_VOLUME *WholeDiskVolume, MBR_PARTITION_INFO *MbrEntry)